Silhouette analysis
set.seed(322)
k.max <- 10
data <- feature_vector_training
nrow(data)
[1] 3322
sil <- rep(0, k.max)
# Compute the average silhouette width for
# k = 2 to k = 15
for(i in 2:k.max){
km.res <- kmeans(data, centers = i, nstart = 25)
ss <- silhouette(km.res$cluster, dist(data))
sil[i] <- mean(ss[, 3])
}
# Plot the average silhouette width
plot(1:k.max, sil, type = "b", pch = 19,
frame = FALSE, xlab = "Number of clusters k")
abline(v = which.max(sil), lty = 2)

Useful functions
cold_start_data <- function(training.sampled,testing,settings){
library(doParallel)
cl <- makeCluster(2)
registerDoParallel(cl)
size_training <- nrow(training.sampled)
split_size_training = size_training / 200
testing_result = data.frame(numeric(nrow(testing)))
count_random <- foreach(i=1:split_size_training) %dopar% {
200 * i
}
metric <- numeric(split_size_training)
metric_t <- numeric(split_size_training)
#metric <- foreach(i=1:split_size_training) %do% {
for(i in c(1:split_size_training)){
#library(caret)
#library(dplyr)
count <- 200 * i
aux_training_set <- training.sampled[c(1:count), ]#training[sample(size_training, count), ]
clusters <- kmeans(aux_training_set[,-c(11,12,13,14)],3,nstart = 25)
aux_training_set_cluster <- cbind(aux_training_set, cluster = clusters$cluster)
result_vector <- numeric(nrow(testing))
result_vector_trainning <- numeric(nrow(aux_training_set))
for (j in c(1:3)){
cluster_data <- dplyr::filter(aux_training_set_cluster, cluster == j)
new_rfFit <- train(subclass ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
data = cluster_data,
metric="ROC",
method = "rf",
trControl = settings)
#Testing predict
predsrfprobs <- predict(new_rfFit,testing,type='prob')
for (k in c(1:length(result_vector))){
if(predsrfprobs$botnet[k] > 0.5){
result_vector[k] <- result_vector[k] + 1
}
else{
result_vector[k] <- result_vector[k] - 1
}
}
#Trainning predict
predsrfprobs_t <- predict(new_rfFit,aux_training_set,type='prob')
for (k in c(1:length(result_vector_trainning))){
if(predsrfprobs_t$botnet[k] > 0.5){
result_vector_trainning[k] <- result_vector_trainning[k] + 1
}
else{
result_vector_trainning[k] <- result_vector_trainning[k] - 1
}
}
}
a = ifelse(result_vector > 0,'botnet','normal')
b <- ifelse(result_vector_trainning > 0,'botnet','normal')
testing_result <- cbind(testing_result,'result' = result_vector)
cm <- confusionMatrix(a,testing$subclass)
metric[i] <- cm$byClass['F1']#cm$overall[1]
cm_t <- confusionMatrix(b,aux_training_set$subclass)
metric_t[i] <- cm_t$byClass['F1']
#list('metric' = metric, 'metric_t' = metric_t)
}
output <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric))
output_t <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric_t))
list_result <- list('output' = output, 'output_t' = output_t, 'testing_result' = testing_result)
}
cold_start_data_only_rf <- function(training.sampled,testing,settings){
size_training <- nrow(training.sampled)
split_size_training = size_training / 200
testing_result = data.frame(numeric(nrow(testing)))
count_random <- foreach(i=1:split_size_training) %dopar% {
200 * i
}
metric <- numeric(split_size_training)
metric_t <- numeric(split_size_training)
#metric <- foreach(i=1:split_size_training) %do% {
for(i in c(1:split_size_training)){
#library(caret)
#library(dplyr)
count <- 200 * i
aux_training_set <- training.sampled[c(1:count), ]#training[sample(size_training, count), ]
new_rfFit <- train(subclass ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
data = aux_training_set,
metric="ROC",
method = "rf",
trControl = settings)
#Testing predict
predsrfprobs <- predict(new_rfFit,testing,type='prob')
predsrf <- ifelse(predsrfprobs$botnet >=0.5,'botnet','normal')
cm <- confusionMatrix(predsrf,testing$subclass)
metric[i] <- cm$byClass['F1']
#Trainning predict
predsrfprobs_t <- predict(new_rfFit,aux_training_set,type='prob')
predsrf_t <- ifelse(predsrfprobs_t$botnet >= 0.5,'botnet','normal')
cm_t <- confusionMatrix(predsrf_t,aux_training_set$subclass)
metric_t[i] <- cm_t$byClass['F1']
}
output <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric))
output_t <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric_t))
list_result <- list('output' = output, 'output_t' = output_t, 'testing_result' = testing_result)
}
generate_data_noisy <- function(dataset, porcent){
list_aux <- sample(nrow(dataset) ,porcent)
noisy_data_sample <- dataset[list_aux,]
no_noisy_data_sample <- dataset[-list_aux,]
noisy_data_sample_b <- noisy_data_sample %>% filter(class == 'Botnet')
noisy_data_sample_n <- noisy_data_sample %>% filter(class == 'Normal')
noisy_data_sample_b$class <- as.character(noisy_data_sample_b$class)
noisy_data_sample_b$class[noisy_data_sample_b$class == 'Botnet'] <- 'Normal'
noisy_data_sample_b$class <- as.factor(noisy_data_sample_b$class)
noisy_data_sample_n$class <- as.character(noisy_data_sample_n$class)
noisy_data_sample_n$class[noisy_data_sample_n$class == 'Normal'] <- 'Botnet'
noisy_data_sample_n$class <- as.factor(noisy_data_sample_n$class)
noisy_data <- rbind(noisy_data_sample_b, noisy_data_sample_n)
training_noisy <- rbind(no_noisy_data_sample,noisy_data)
training_noisy <- training_noisy[sample(nrow(training_noisy),nrow(training_noisy)),]
return(training_noisy)
}
get_ELA_measure <- function(A0, Ax){
RLA <- (A0 - Ax) / A0
FA0 <- (100 - A0) / A0
ELA <- RLA + FA0
return(ELA)
}
randomForest_performace <- function(training_data, testing_data){
rfFit <- train(class ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
data = training_data,
metric="ROC",
method = "rf",
trControl = settings)
predsrfprobs <- predict(rfFit,testing_data,type='prob')
predsrf <- ifelse(predsrfprobs$Botnet >=0.5,'Botnet','Normal')
cm <- confusionMatrix(predsrf,testing_data$class)
result <- cm$byClass
return(result)
}
training
testing
Data training partitions: cold start study
Iteration #1
output_1 <- result$output
output_t_1 <- result$output_t
output_1
gg <- ggplot(data = output_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

first_training_sample <- training.sampled_1[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

output_t_aux_1 <- output_t_1
names(output_t_aux_1) <- c('data_count_t','metric_t')
output_result_1 <- cbind(output_1,output_t_aux_1)
gg <- ggplot(data = output_result_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Iteration #2
output_2 <- result_2$output
output_t_2 <- result_2$output_t
output_2
gg <- ggplot(data = output_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

first_training_sample <- training.sampled_2[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result_2 <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

output_t_aux_2 <- output_t_2
names(output_t_aux_2) <- c('data_count_t','metric_t')
output_result_2 <- cbind(output_2,output_t_aux_2)
gg <- ggplot(data = output_result_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Iteration #3
output_3 <- result_3$output
output_t_3 <- result_3$output_t
output_3
gg <- ggplot(data = output_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

first_training_sample <- training.sampled_3[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result_3 <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

output_t_aux_3 <- output_t_3
names(output_t_aux_3) <- c('data_count_t','metric_t')
output_result_3 <- cbind(output_3,output_t_aux_3)
gg <- ggplot(data = output_result_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Iteration #4
output_4 <- result_4$output
output_t_4 <- result_4$output_t
output_4
gg <- ggplot(data = output_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

first_training_sample <- training.sampled_4[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result_4 <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

output_t_aux_4 <- output_t_4
names(output_t_aux_4) <- c('data_count_t','metric_t')
output_result_4 <- cbind(output_4,output_t_aux_4)
gg <- ggplot(data = output_result_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Iteration #5
output_5 <- result_5$output
output_t_5 <- result_5$output_t
output_5
gg <- ggplot(data = output_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

first_training_sample <- training.sampled_5[1:200,]
ggplot(first_training_sample) + geom_bar(aes(subclass))

class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

testing_result_5 <- result$testing_result
#testing_result
gg <- ggplot(data = output_t_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

output_t_aux_5 <- output_t_5
names(output_t_aux_5) <- c('data_count_t','metric_t')
output_result_5 <- cbind(output_5,output_t_aux_5)
gg <- ggplot(data = output_result_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Data training partitions: cold start study (simple Random Forest)
Iteration #1
rf_output_1 <- rf_result_1$output
rf_output_t_1 <- rf_result_1$output_t
rf_output_1
gg <- ggplot(data = rf_output_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_first_training_sample <- rf_training.sampled_1[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_1 <- rf_result_1$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_output_t_aux_1 <- rf_output_t_1
names(rf_output_t_aux_1) <- c('data_count_t','metric_t')
rf_output_result_1 <- cbind(rf_output_1,rf_output_t_aux_1)
gg <- ggplot(data = rf_output_result_1)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Iteration #2
rf_output_2 <- rf_result_2$output
rf_output_t_2 <- rf_result_2$output_t
rf_output_2
gg <- ggplot(data = rf_output_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_first_training_sample <- rf_training.sampled_2[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_2 <- rf_result_2$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_output_t_aux_2 <- rf_output_t_2
names(rf_output_t_aux_2) <- c('data_count_t','metric_t')
rf_output_result_2 <- cbind(rf_output_2,rf_output_t_aux_2)
gg <- ggplot(data = rf_output_result_2)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Iteration #3
rf_output_3 <- rf_result_3$output
rf_output_t_3 <- rf_result_3$output_t
rf_output_3
gg <- ggplot(data = rf_output_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_first_training_sample <- rf_training.sampled_3[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_2 <- rf_result_2$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_output_t_aux_3 <- rf_output_t_3
names(rf_output_t_aux_3) <- c('data_count_t','metric_t')
rf_output_result_3 <- cbind(rf_output_3,rf_output_t_aux_3)
gg <- ggplot(data = rf_output_result_3)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Iteration #4
rf_output_4 <- rf_result_4$output
rf_output_t_4 <- rf_result_4$output_t
rf_output_4
gg <- ggplot(data = rf_output_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_first_training_sample <- rf_training.sampled_4[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_2 <- rf_result_2$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_output_t_aux_4 <- rf_output_t_4
names(rf_output_t_aux_4) <- c('data_count_t','metric_t')
rf_output_result_4 <- cbind(rf_output_4,rf_output_t_aux_4)
gg <- ggplot(data = rf_output_result_4)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Iteration #5
rf_output_5 <- rf_result_5$output
rf_output_t_5 <- rf_result_5$output_t
rf_output_5
gg <- ggplot(data = rf_output_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_first_training_sample <- rf_training.sampled_5[1:200,]
ggplot(rf_first_training_sample) + geom_bar(aes(subclass))

rf_class_distribution <- rf_first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(rf_first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))

#rf_testing_result_2 <- rf_result_2$testing_result
#testing_result
gg <- ggplot(data = rf_output_t_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

rf_output_t_aux_5 <- rf_output_t_5
names(rf_output_t_aux_5) <- c('data_count_t','metric_t')
rf_output_result_5 <- cbind(rf_output_5,rf_output_t_aux_5)
gg <- ggplot(data = rf_output_result_5)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') + geom_line(aes(x = data_count_t, y = metric_t),color = 'blue') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="F1 Score",
color=NULL)

Studies Samples
first_training_sample <- training.sampled[1:200,]
first_training_sample
ggplot(first_training_sample) + geom_bar(aes(subclass))
class_distribution <- first_training_sample %>% group_by(class) %>% summarise(n = n()) %>% arrange(desc(n))
ggplot(first_training_sample) + geom_bar(aes(class)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))
part 2
set.seed(206)
library(doParallel)
cl <- makeCluster(2)
registerDoParallel(cl)
size_training <- nrow(training)
split_size_training = size_training / 200
count_random <- foreach(i=1:split_size_training) %dopar% {
200 * i
}
training.sampled <- training[sample(size_training, size_training), ]
metric <- foreach(i=1:split_size_training) %do% {
#library(caret)
count <- 200 * i
aux_training_set <- training.sampled[c(1:count), ]#training[sample(size_training, count), ]
clusters <- kmeans(aux_training_set[,-c(11,12,13,14)],3,nstart = 25)
aux_training_set_cluster <- cbind(aux_training_set, cluster = clusters$cluster)
result_vector <- numeric(nrow(testing))
for (j in c(1:3)){
cluster_data <- filter(aux_training_set_cluster, cluster == j)
new_rfFit <- train(class ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
data = cluster_data,
metric="ROC",
method = "rf",
trControl = ctrl_fast)
predsrfprobs <- predict(new_rfFit,testing,type='prob')
for (k in c(1:length(result_vector))){
if(predsrfprobs$Botnet[k] > 0.5){
result_vector[k] <- result_vector[k] + 1
}
else{
result_vector[k] <- result_vector[k] - 1
}
}
}
a = ifelse(result_vector > 0,'Botnet','Normal')
cm <- confusionMatrix(a,testing$class)
metric <- cm$byClass['F1']#cm$overall[1]
metric
}
output <- do.call(rbind, Map(data.frame, data_count=count_random, metric=metric))
output
gg <- ggplot(data = output)
gg + geom_line(aes(x = data_count, y = metric),color = 'red') +
labs(title="Random Forest through data training size",
#subtitle="Drawn from Long Data format",
caption="Source: CTU-13",
y="Accuracy",
color=NULL)
cluster_data
Test with only one
set.seed(226)
size_training <- nrow(training)
training.sampled <- training[sample(size_training, size_training), ]
aux_training_set <- training.sampled[c(1:200), ]#training[sample(size_training, 200), ]
clusters <- kmeans(aux_training_set[,-c(11,12,13,14)],3,nstart = 25)
aux_training_set_cluster <- cbind(aux_training_set, cluster = clusters$cluster)
result_vector <- numeric(nrow(testing))
result_vector_trainning <- numeric(nrow(aux_training_set))
for (j in c(1:3)){
cluster_data <- aux_training_set_cluster %>% filter(cluster == j)
new_rfFit <- train(subclass ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl,
data = cluster_data,
metric="ROC",
method = "rf",
trControl = ctrl_fast)
predsrfprobs <- predict(new_rfFit,testing,type='prob')
for (k in c(1:length(result_vector))){
if(predsrfprobs$botnet[k] > 0.5){
result_vector[k] <- result_vector[k] + 1
}
else{
result_vector[k] <- result_vector[k] - 1
}
}
#Trainning predict
predsrfprobs_t <- predict(new_rfFit,aux_training_set,type='prob')
for (k in c(1:length(result_vector_trainning))){
if(predsrfprobs_t$botnet[k] > 0.5){
result_vector_trainning[k] <- result_vector_trainning[k] + 1
}
else{
result_vector_trainning[k] <- result_vector_trainning[k] - 1
}
}
}
a = ifelse(result_vector > 0,'botnet','normal')
b <- ifelse(result_vector_trainning > 0,'botnet','normal')
cm <- confusionMatrix(a,testing$subclass)
metric <- cm$byClass['F1']#cm$overall[1]
metric
cm_t <- confusionMatrix(b,aux_training_set$subclass)
metric_t <- cm_t$byClass['F1']
metric_t
Sample examples
set.seed(556)
a = c(1,2,3,4,5,6,7,8,9)
r <- sample(9,3)
a[r]
r2 <- sample(9,3)
a[r2]
#testing_result
testing_result.bkp <- testing_result
testing_result
names_aux <- foreach(i=1:(nrow(training)/200)) %do% {
iteration <- 200 * i
paste('size_',toString(iteration),sep = "")
}
testing_result_names <- unlist(names_aux, use.names=FALSE)
testing_result <- testing_result[,c(-1)]
names(testing_result) <- testing_result_names
testing_result
testing_aux <- cbind(testing,testing_result)
testing_aux.bkp2 <- testing_aux
#write.table(testing_aux,file="testing_cluster_result.txt",sep="|", row.names = F)
testing_aux
sums <- rowSums(testing_aux[,-c(1:14)])
sums
testing_aux[,-c(1:14)]
testing_aux <- cbind(testing_aux,sums)
testing_aux
testing_aux_result <- testing_aux %>% group_by(class) %>% summarise(n = n(), sums = sum(sums)) %>% arrange(desc(sums))
testing_aux_result
graph_testing_result <- ggplot(testing_aux_result[-c(1,nrow(testing_aux_result)),])
graph_testing_result + geom_point(aes(class,sums)) + theme(axis.text.x = element_text(angle = 90, hjust = 1))
feature_vectors_cleaned
library(gridExtra)
pdf("data_output.pdf", height=11, width=8.5)
grid.table(feature_vectors_cleaned[1:20,])
dev.off()
testing_result.bkp
testing_aux.bkp2
testing_aux_result
rusty_data_result <- testing_aux.bkp2
rusty_data_result_short <- rusty_data_result[,-c(1:11,14)]
rusty_data_result_short[,-c(1,2)]
rusty_data_result_short$pos <- rowSums(rusty_data_result_short[,-c(1,2)] > 0)
rusty_data_result_short$neg <- rowSums(rusty_data_result_short[,-c(1,2)] < 0)
rusty_data_result_short_cleaned <- rusty_data_result_short[,c(1,2,46,47)]
rusty_data_result_short_cleaned
rusty_data_result_short_cleaned_result <- rusty_data_result_short_cleaned %>% mutate(good = ifelse(subclass == 'normal',neg,pos))
rusty_data_result_short_cleaned_result <- rusty_data_result_short_cleaned_result %>% mutate(bad = ifelse(subclass == 'normal',pos,neg))
rusty_data_result_short_cleaned_result %>% group_by(port) %>% summarise(n=n(),good = sum(good),bad = sum(bad)) %>% arrange(desc(n))
data_botnet_port <- rusty_data_result_short_cleaned_result %>% filter(subclass == 'botnet')
data_normal_port <- rusty_data_result_short_cleaned_result %>% filter(subclass == 'normal')
data_botnet_port_result <- data_botnet_port %>% group_by(port) %>% summarise(n=n(),good = sum(good),bad = sum(bad)) %>% arrange(desc(n))
data_normal_port_result <- data_normal_port %>% group_by(port) %>% summarise(n=n(),good = sum(good),bad = sum(bad)) %>% arrange(desc(n))
data_botnet_port_result
data_normal_port_result
ggplot(data = data_botnet_port_result) +
geom_bar(mapping = aes(x = port, fill = clarity))
#write.table(data_botnet_port_result,file="data_botnet_port.txt",sep="|", row.names = F)
library(reshape2)
data <- data_botnet_port_result
data$port <- as.factor(data$port)
melt(data[,c(1,3,4)])
ggplot(melt(data[,c(1,3,4)]))+
geom_col(aes(x=port,y=value,fill=variable))+
#theme_bw()+
theme(axis.text.x = element_text(angle = 45, hjust = 1))
LS0tCnRpdGxlOiAiQ0FJJ3MgZXhwZXJpbWVudHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBMaWJyYXJ5IEVudmlyb25tZW50CmBgYHtyfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkodGlkeXZlcnNlKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHN0cmluZ3IpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoSVNMUikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShjYXJldCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkb01DKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHBsb3RseSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShzdHJpbmdyKSkKcmVnaXN0ZXJEb01DKGNvcmVzPTQpCmBgYAoKIyMjIExvYWQgYW5kIHByb2Nlc3NpbmcgZGF0YSBjdHUxMyBjbGVhbmVkCmBgYHtyfQpteURhdGFfY2xlYW5lZCA8LSByZWFkLmNzdignL2hvbWUvamd1ZXJyYS9kYXRhc2V0cy9jdHUxMy5sYWJlbGVkLmNsZWFuZWQnLCBzdHJpbmdzQXNGYWN0b3JzID0gRiwgc2VwID0gJ3wnKQpteURhdGFfY2xlYW5lZC5ia3AgPSBteURhdGFfY2xlYW5lZApteURhdGFfY2xlYW5lZAoKI1BlcmlvZGljaXR5Cm15RGF0YV9jbGVhbmVkID0gbXlEYXRhX2NsZWFuZWQgJT4lIG11dGF0ZShzdHJvbmdfcCA9IHN0cl9jb3VudChTdGF0ZSwnW2EtaV0nKSkKbXlEYXRhX2NsZWFuZWQgPSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHdlYWtfcCA9IHN0cl9jb3VudChTdGF0ZSwnW0EtSV0nKSkKbXlEYXRhX2NsZWFuZWQgPSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHdlYWtfbnAgPSBzdHJfY291bnQoU3RhdGUsJ1tyLXpdJykpCm15RGF0YV9jbGVhbmVkID0gbXlEYXRhX2NsZWFuZWQgJT4lIG11dGF0ZShzdHJvbmdfbnAgPSBzdHJfY291bnQoU3RhdGUsJ1tSLVpdJykpCiNEdXJhdGlvbgpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoZHVyYXRpb25fcyA9IHN0cl9jb3VudChTdGF0ZSwnKGF8QXxyfFJ8MXxkfER8dXxVfDR8Z3xHfHh8WHw3KScpKQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoZHVyYXRpb25fbSA9IHN0cl9jb3VudChTdGF0ZSwnKGJ8QnxzfFN8MnxlfEV8dnxWfDV8aHxIfHl8WXw4KScpKQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoZHVyYXRpb25fbCA9IHN0cl9jb3VudChTdGF0ZSwnKGN8Q3x0fFR8M3xmfEZ8d3xXfDZ8aXxJfHp8Wnw5KScpKQojU2l6ZQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoc2l6ZV9zID0gc3RyX2NvdW50KFN0YXRlLCdbYS1jXScpICsgc3RyX2NvdW50KFN0YXRlLCdbQS1DXScpICsgc3RyX2NvdW50KFN0YXRlLCdbci10XScpICsgc3RyX2NvdW50KFN0YXRlLCdbUi1UXScpICsgc3RyX2NvdW50KFN0YXRlLCdbMS0zXScpKQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoc2l6ZV9tID0gc3RyX2NvdW50KFN0YXRlLCdbZC1mXScpICsgc3RyX2NvdW50KFN0YXRlLCdbRC1GXScpICsgc3RyX2NvdW50KFN0YXRlLCdbdS13XScpICsgc3RyX2NvdW50KFN0YXRlLCdbVS1XXScpICsgc3RyX2NvdW50KFN0YXRlLCdbNC02XScpKQpteURhdGFfY2xlYW5lZCA9IG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoc2l6ZV9sID0gc3RyX2NvdW50KFN0YXRlLCdbZy1pXScpICsgc3RyX2NvdW50KFN0YXRlLCdbRy1JXScpICsgc3RyX2NvdW50KFN0YXRlLCdbeC16XScpICsgc3RyX2NvdW50KFN0YXRlLCdbWC1aXScpICsgc3RyX2NvdW50KFN0YXRlLCdbNy05XScpKQoKI1BlcmlvZGljaXR5ICUKbXlEYXRhX2NsZWFuZWQgPC0gbXlEYXRhX2NsZWFuZWQgJT4lIG11dGF0ZShzdHJvbmdfcCA9IChzdHJvbmdfcCAvIG1vZGVsc2l6ZSkpCm15RGF0YV9jbGVhbmVkIDwtIG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUod2Vha19wID0gKHdlYWtfcCAvIG1vZGVsc2l6ZSkpCm15RGF0YV9jbGVhbmVkIDwtIG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoc3Ryb25nX25wID0gKHN0cm9uZ19ucCAvIG1vZGVsc2l6ZSkpCm15RGF0YV9jbGVhbmVkIDwtIG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUod2Vha19ucCA9ICh3ZWFrX25wIC8gbW9kZWxzaXplKSkKI0R1cmF0aW9uICUKbXlEYXRhX2NsZWFuZWQgPC0gbXlEYXRhX2NsZWFuZWQgJT4lIG11dGF0ZShkdXJhdGlvbl9zID0gKGR1cmF0aW9uX3MgLyBtb2RlbHNpemUpKQpteURhdGFfY2xlYW5lZCA8LSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKGR1cmF0aW9uX20gPSAoZHVyYXRpb25fbSAvIG1vZGVsc2l6ZSkpCm15RGF0YV9jbGVhbmVkIDwtIG15RGF0YV9jbGVhbmVkICU+JSBtdXRhdGUoZHVyYXRpb25fbCA9IChkdXJhdGlvbl9sIC8gbW9kZWxzaXplKSkKI1NpemUgJQpteURhdGFfY2xlYW5lZCA8LSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHNpemVfcyA9IChzaXplX3MgLyBtb2RlbHNpemUpKQpteURhdGFfY2xlYW5lZCA8LSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHNpemVfbSA9IChzaXplX20gLyBtb2RlbHNpemUpKQpteURhdGFfY2xlYW5lZCA8LSBteURhdGFfY2xlYW5lZCAlPiUgbXV0YXRlKHNpemVfbCA9IChzaXplX2wgLyBtb2RlbHNpemUpKQoKI01ha2luZyBmZWF0dXJlIHZlY3RvcnMKZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQgPSBteURhdGFfY2xlYW5lZFssYygnc3Ryb25nX3AnLCd3ZWFrX3AnLCd3ZWFrX25wJywnc3Ryb25nX25wJywnZHVyYXRpb25fcycsJ2R1cmF0aW9uX20nLCdkdXJhdGlvbl9sJywnc2l6ZV9zJywnc2l6ZV9tJywnc2l6ZV9sJywnbW9kZWxzaXplJywnbGFiZWwnLCdjbGFzcycsJ3BvcnQnLCdwcm90bycpXQpuYW1lcyhmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCkgPSBjKCJzcCIsIndwIiwid25wIiwic25wIiwiZHMiLCJkbSIsImRsIiwic3MiLCJzbSIsInNsIiwibW9kZWxzaXplIiwiY2xhc3MiLCJzdWJjbGFzcyIsInBvcnQiLCJwcm90byIpCmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkJGNsYXNzID0gZmFjdG9yKGZlYXR1cmVfdmVjdG9yc19jbGVhbmVkJGNsYXNzKQpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCRzdWJjbGFzcyA9IGZhY3RvcihmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCRzdWJjbGFzcykKZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQkcHJvdG8gPSBmYWN0b3IoZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQkcHJvdG8pCgpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZAoKYGBgCgojIyMgUmVtb3ZpbmcgZXhjZXNpdmUgQm90bmV0IGFuZCBOb3JtYWwgY2xhc3MoTWFraW5nIHRoZSBkYXRhc2V0IG1vcmUgZXF1aXRhYmxlKQpgYGB7cn0KZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQuYmtwIDwtIGZlYXR1cmVfdmVjdG9yc19jbGVhbmVkCmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuPW4oKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWRfYXV4X2JvdG5ldCA8LSBmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCAlPiUgZmlsdGVyKGNsYXNzID09ICdCb3RuZXQtVENQLVNNVFAtQXR0ZW1wdC1TUEFNJykKZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWRfYXV4X25vcm1hbCA8LSBmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCAlPiUgZmlsdGVyKGNsYXNzID09ICdOb3JtYWwtVENQLUhUVFAnKQpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZF9hdXhfYm90bmV0CmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkX2F1eF9ub3JtYWwKCmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkX2F1eF9yZXN0IDwtIGZlYXR1cmVfdmVjdG9yc19jbGVhbmVkICU+JSBmaWx0ZXIoY2xhc3MgIT0gJ0JvdG5ldC1UQ1AtU01UUC1BdHRlbXB0LVNQQU0nKSAlPiUgZmlsdGVyKGNsYXNzICE9ICdOb3JtYWwtVENQLUhUVFAnKQpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZF9hdXhfcmVzdCAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2Uobj1uKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCmF1eDEgPC0gcmJpbmQoZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWRfYXV4X2JvdG5ldFsxOjUwMCxdLGZlYXR1cmVfdmVjdG9yc19jbGVhbmVkX2F1eF9ub3JtYWxbMTo1MDAsXSkKYXV4MSAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2Uobj1uKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCmF1eCA8LSByYmluZChmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZF9hdXhfcmVzdCxhdXgxKQphdXggJT4lIGdyb3VwX2J5KGNsYXNzKSAlPiUgc3VtbWFyaXNlKG49bigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZCA8LSBhdXgKYGBgCgojIyMgQ3JlYXRlIHRyYWluaW5nIHNldCBhbmQgdGVzdHNldApgYGB7cn0Kc2V0LnNlZWQoMjEyKQp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQkc3ViY2xhc3MsIHA9MC43MCwgbGlzdD1GQUxTRSkKZGF0YV90cmFpbmluZyA8LSBmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZFsgdHJhaW5JbmRleCxdCmRhdGFfdGVzdGluZyA8LSBmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZFstdHJhaW5JbmRleCxdCgojZGF0YV90cmFpbiA9IGRhdGFfdHJhaW4gJT4lIGZpbHRlcihsZW5ndGg+NSkKdHJhaW4gPC0gdXBTYW1wbGUoeCA9IGRhdGFfdHJhaW5pbmcsICB5ID0gZGF0YV90cmFpbmluZyRzdWJjbGFzcywgeW5hbWU9ImNsYXNzIikKCnRyYWluaW5nIDwtIHRyYWluWywtYygxMSwxNildCnRlc3RpbmcgPC0gZGF0YV90ZXN0aW5nWywtYygxMSldCnRyYWluaW5nCnRlc3RpbmcKCm5yb3codHJhaW5pbmcpCm5yb3coZmVhdHVyZV92ZWN0b3JzX2NsZWFuZWQpCgpgYGAKCiMjIyBUcmFpbmluZyBjb25maWd1cmF0aW9uCmBgYHtyfQpjdHJsX2Zhc3QgPC0gdHJhaW5Db250cm9sKG1ldGhvZD0iY3YiLCAKICAgICAgICAgICAgICAgICAgICAgcmVwZWF0cz0yLAogICAgICAgICAgICAgICAgICAgICBudW1iZXI9MTAsIAogICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5RnVuY3Rpb249dHdvQ2xhc3NTdW1tYXJ5LAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlSXRlcj1ULAogICAgICAgICAgICAgICAgICAgICBjbGFzc1Byb2JzPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGFsbG93UGFyYWxsZWwgPSBUUlVFKSAgCmBgYAoKIyMjIEV4cGVyaW1lbnQgMQojIyBDcmVhdGlvbiBvZiBjbHVzdGVyIGFuZCBrIHBhcmFtZXRlcnMgYW5hbHlzaXMKYGBge3J9CmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShjbHVzdGVyKQpsaWJyYXJ5KE5iQ2x1c3QpCmZlYXR1cmVfdmVjdG9yX3RyYWluaW5nID0gdHJhaW5pbmdbLC1jKDExLDEyLDEzLDE0KV0KIyBLLW1lYW5zIGNsdXN0ZXJpbmcKc2V0LnNlZWQoMzIxKQoja20ucmVzIDwtIGttZWFucyhmZWF0dXJlX3ZlY3Rvcl90cmFpbmluZywgMywgbnN0YXJ0ID0gMjUpCmttLnJlcyA8LSBrbWVhbnMoZmVhdHVyZV92ZWN0b3JfdHJhaW5pbmcsIDcsIG5zdGFydCA9IDI1KQojIGstbWVhbnMgZ3JvdXAgbnVtYmVyIG9mIGVhY2ggb2JzZXJ2YXRpb24Ka20ucmVzJGNsdXN0ZXIKCiMgVmlzdWFsaXplIGstbWVhbnMgY2x1c3RlcnMKZnZpel9jbHVzdGVyKGttLnJlcywgZGF0YSA9IGZlYXR1cmVfdmVjdG9yX3RyYWluaW5nLCBnZW9tID0gInBvaW50IiwKICAgICAgICAgICAgIHN0YW5kID0gRkFMU0UsIGVsbGlwc2UudHlwZSA9ICJub3JtIikKYGBgCiMjIyBFbGJvdyBhbmFseXNpcwpgYGB7cn0Kc2V0LnNlZWQoMzIxKQojIENvbXB1dGUgYW5kIHBsb3Qgd3NzIGZvciBrID0gMiB0byBrID0gMTUKay5tYXggPC0gMTUgIyBNYXhpbWFsIG51bWJlciBvZiBjbHVzdGVycwpkYXRhIDwtIGZlYXR1cmVfdmVjdG9yX3RyYWluaW5nCndzcyA8LSBzYXBwbHkoMTprLm1heCwgCiAgICAgICAgZnVuY3Rpb24oayl7a21lYW5zKGRhdGEsIGssIG5zdGFydD0xMCApJHRvdC53aXRoaW5zc30pCnBsb3QoMTprLm1heCwgd3NzLAogICAgICAgdHlwZT0iYiIsIHBjaCA9IDE5LCBmcmFtZSA9IEZBTFNFLCAKICAgICAgIHhsYWI9Ik51bWJlciBvZiBjbHVzdGVycyBLIiwKICAgICAgIHlsYWI9IlRvdGFsIHdpdGhpbi1jbHVzdGVycyBzdW0gb2Ygc3F1YXJlcyIpCmFibGluZSh2ID0gMywgbHR5ID0yKQpgYGAKIyMgU2lsaG91ZXR0ZSBhbmFseXNpcwpgYGB7cn0Kc2V0LnNlZWQoMzIyKQprLm1heCA8LSAxMApkYXRhIDwtIGZlYXR1cmVfdmVjdG9yX3RyYWluaW5nCm5yb3coZGF0YSkKc2lsIDwtIHJlcCgwLCBrLm1heCkKIyBDb21wdXRlIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggZm9yIAojIGsgPSAyIHRvIGsgPSAxNQoKZm9yKGkgaW4gMjprLm1heCl7CiAga20ucmVzIDwtIGttZWFucyhkYXRhLCBjZW50ZXJzID0gaSwgbnN0YXJ0ID0gMjUpCiAgc3MgPC0gc2lsaG91ZXR0ZShrbS5yZXMkY2x1c3RlciwgZGlzdChkYXRhKSkKICBzaWxbaV0gPC0gbWVhbihzc1ssIDNdKQp9CiMgUGxvdCB0aGUgIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aApwbG90KDE6ay5tYXgsIHNpbCwgdHlwZSA9ICJiIiwgcGNoID0gMTksIAogICAgIGZyYW1lID0gRkFMU0UsIHhsYWIgPSAiTnVtYmVyIG9mIGNsdXN0ZXJzIGsiKQphYmxpbmUodiA9IHdoaWNoLm1heChzaWwpLCBsdHkgPSAyKQoKYGBgCiMjIyBVc2VmdWwgZnVuY3Rpb25zCmBgYHtyfQpjb2xkX3N0YXJ0X2RhdGEgPC0gZnVuY3Rpb24odHJhaW5pbmcuc2FtcGxlZCx0ZXN0aW5nLHNldHRpbmdzKXsKICBsaWJyYXJ5KGRvUGFyYWxsZWwpCiAgY2wgPC0gbWFrZUNsdXN0ZXIoMikKICByZWdpc3RlckRvUGFyYWxsZWwoY2wpCiAgc2l6ZV90cmFpbmluZyA8LSBucm93KHRyYWluaW5nLnNhbXBsZWQpCiAgc3BsaXRfc2l6ZV90cmFpbmluZyA9IHNpemVfdHJhaW5pbmcgLyAyMDAKICB0ZXN0aW5nX3Jlc3VsdCA9IGRhdGEuZnJhbWUobnVtZXJpYyhucm93KHRlc3RpbmcpKSkKICAKICBjb3VudF9yYW5kb20gPC0gZm9yZWFjaChpPTE6c3BsaXRfc2l6ZV90cmFpbmluZykgJWRvcGFyJSB7CiAgICAyMDAgKiBpCiAgfQogIG1ldHJpYyA8LSBudW1lcmljKHNwbGl0X3NpemVfdHJhaW5pbmcpCiAgbWV0cmljX3QgPC0gbnVtZXJpYyhzcGxpdF9zaXplX3RyYWluaW5nKQogICNtZXRyaWMgPC0gZm9yZWFjaChpPTE6c3BsaXRfc2l6ZV90cmFpbmluZykgJWRvJSB7CiAgZm9yKGkgaW4gYygxOnNwbGl0X3NpemVfdHJhaW5pbmcpKXsKICAgICNsaWJyYXJ5KGNhcmV0KQogICAgI2xpYnJhcnkoZHBseXIpCiAgICBjb3VudCA8LSAyMDAgKiBpCiAgICBhdXhfdHJhaW5pbmdfc2V0IDwtIHRyYWluaW5nLnNhbXBsZWRbYygxOmNvdW50KSwgXSN0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgY291bnQpLCBdCiAgICBjbHVzdGVycyA8LSBrbWVhbnMoYXV4X3RyYWluaW5nX3NldFssLWMoMTEsMTIsMTMsMTQpXSwzLG5zdGFydCA9IDI1KQogICAgYXV4X3RyYWluaW5nX3NldF9jbHVzdGVyIDwtIGNiaW5kKGF1eF90cmFpbmluZ19zZXQsIGNsdXN0ZXIgPSBjbHVzdGVycyRjbHVzdGVyKQogICAgcmVzdWx0X3ZlY3RvciA8LSBudW1lcmljKG5yb3codGVzdGluZykpCiAgICByZXN1bHRfdmVjdG9yX3RyYWlubmluZyA8LSBudW1lcmljKG5yb3coYXV4X3RyYWluaW5nX3NldCkpCiAgICAKICAgIGZvciAoaiBpbiBjKDE6MykpewogICAgICBjbHVzdGVyX2RhdGEgPC0gZHBseXI6OmZpbHRlcihhdXhfdHJhaW5pbmdfc2V0X2NsdXN0ZXIsIGNsdXN0ZXIgPT0gaikKICAgICAgbmV3X3JmRml0IDwtIHRyYWluKHN1YmNsYXNzIH4gc3Ard3Ard25wK3NucCtkcytkbStkbCtzcytzbStzbCwKICAgICAgICAgICAgICAgICBkYXRhID0gY2x1c3Rlcl9kYXRhLAogICAgICAgICAgICAgICAgIG1ldHJpYz0iUk9DIiwKICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHNldHRpbmdzKQogICAgICAjVGVzdGluZyBwcmVkaWN0CiAgICAgIHByZWRzcmZwcm9icyA8LSBwcmVkaWN0KG5ld19yZkZpdCx0ZXN0aW5nLHR5cGU9J3Byb2InKQogICAgICAKICAgICAgZm9yIChrIGluIGMoMTpsZW5ndGgocmVzdWx0X3ZlY3RvcikpKXsKICAgICAgICBpZihwcmVkc3JmcHJvYnMkYm90bmV0W2tdID4gMC41KXsKICAgICAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSArIDEKICAgICAgICB9CiAgICAgICAgZWxzZXsKICAgICAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSAtIDEKICAgICAgICB9CiAgICAgIH0KICAgICAgCiAgICAgICNUcmFpbm5pbmcgcHJlZGljdAogICAgICBwcmVkc3JmcHJvYnNfdCA8LSBwcmVkaWN0KG5ld19yZkZpdCxhdXhfdHJhaW5pbmdfc2V0LHR5cGU9J3Byb2InKQogICAgICBmb3IgKGsgaW4gYygxOmxlbmd0aChyZXN1bHRfdmVjdG9yX3RyYWlubmluZykpKXsKICAgICAgICBpZihwcmVkc3JmcHJvYnNfdCRib3RuZXRba10gPiAwLjUpewogICAgICAgICAgcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmdba10gPC0gcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmdba10gKyAxCiAgICAgICAgfQogICAgICAgIGVsc2V7CiAgICAgICAgICByZXN1bHRfdmVjdG9yX3RyYWlubmluZ1trXSA8LSByZXN1bHRfdmVjdG9yX3RyYWlubmluZ1trXSAtIDEKICAgICAgICB9CiAgICAgIH0KICAgIH0KICAgIGEgPSBpZmVsc2UocmVzdWx0X3ZlY3RvciA+IDAsJ2JvdG5ldCcsJ25vcm1hbCcpCiAgICBiIDwtIGlmZWxzZShyZXN1bHRfdmVjdG9yX3RyYWlubmluZyA+IDAsJ2JvdG5ldCcsJ25vcm1hbCcpCiAgICB0ZXN0aW5nX3Jlc3VsdCA8LSBjYmluZCh0ZXN0aW5nX3Jlc3VsdCwncmVzdWx0JyA9IHJlc3VsdF92ZWN0b3IpCiAgICBjbSA8LSBjb25mdXNpb25NYXRyaXgoYSx0ZXN0aW5nJHN1YmNsYXNzKQogICAgbWV0cmljW2ldIDwtIGNtJGJ5Q2xhc3NbJ0YxJ10jY20kb3ZlcmFsbFsxXQogICAgCiAgICBjbV90IDwtIGNvbmZ1c2lvbk1hdHJpeChiLGF1eF90cmFpbmluZ19zZXQkc3ViY2xhc3MpCiAgICBtZXRyaWNfdFtpXSA8LSBjbV90JGJ5Q2xhc3NbJ0YxJ10KICAgICNsaXN0KCdtZXRyaWMnID0gbWV0cmljLCAnbWV0cmljX3QnID0gbWV0cmljX3QpCiAgfQogIG91dHB1dCA8LSBkby5jYWxsKHJiaW5kLCBNYXAoZGF0YS5mcmFtZSwgZGF0YV9jb3VudD1jb3VudF9yYW5kb20sIG1ldHJpYz1tZXRyaWMpKQogIG91dHB1dF90IDwtIGRvLmNhbGwocmJpbmQsIE1hcChkYXRhLmZyYW1lLCBkYXRhX2NvdW50PWNvdW50X3JhbmRvbSwgbWV0cmljPW1ldHJpY190KSkKICBsaXN0X3Jlc3VsdCA8LSBsaXN0KCdvdXRwdXQnID0gb3V0cHV0LCAnb3V0cHV0X3QnID0gb3V0cHV0X3QsICd0ZXN0aW5nX3Jlc3VsdCcgPSB0ZXN0aW5nX3Jlc3VsdCkKfQoKY29sZF9zdGFydF9kYXRhX29ubHlfcmYgPC0gZnVuY3Rpb24odHJhaW5pbmcuc2FtcGxlZCx0ZXN0aW5nLHNldHRpbmdzKXsKICBzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcuc2FtcGxlZCkKICBzcGxpdF9zaXplX3RyYWluaW5nID0gc2l6ZV90cmFpbmluZyAvIDIwMAogIHRlc3RpbmdfcmVzdWx0ID0gZGF0YS5mcmFtZShudW1lcmljKG5yb3codGVzdGluZykpKQogIAogIGNvdW50X3JhbmRvbSA8LSBmb3JlYWNoKGk9MTpzcGxpdF9zaXplX3RyYWluaW5nKSAlZG9wYXIlIHsKICAgIDIwMCAqIGkKICB9CiAgbWV0cmljIDwtIG51bWVyaWMoc3BsaXRfc2l6ZV90cmFpbmluZykKICBtZXRyaWNfdCA8LSBudW1lcmljKHNwbGl0X3NpemVfdHJhaW5pbmcpCiAgI21ldHJpYyA8LSBmb3JlYWNoKGk9MTpzcGxpdF9zaXplX3RyYWluaW5nKSAlZG8lIHsKICBmb3IoaSBpbiBjKDE6c3BsaXRfc2l6ZV90cmFpbmluZykpewogICAgI2xpYnJhcnkoY2FyZXQpCiAgICAjbGlicmFyeShkcGx5cikKICAgIGNvdW50IDwtIDIwMCAqIGkKICAgIGF1eF90cmFpbmluZ19zZXQgPC0gdHJhaW5pbmcuc2FtcGxlZFtjKDE6Y291bnQpLCBdI3RyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBjb3VudCksIF0KICAgIG5ld19yZkZpdCA8LSB0cmFpbihzdWJjbGFzcyB+IHNwK3dwK3ducCtzbnArZHMrZG0rZGwrc3Mrc20rc2wsCiAgICAgICAgICAgICAgICAgZGF0YSA9IGF1eF90cmFpbmluZ19zZXQsCiAgICAgICAgICAgICAgICAgbWV0cmljPSJST0MiLAogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gc2V0dGluZ3MpCiAgICAjVGVzdGluZyBwcmVkaWN0CiAgICBwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChuZXdfcmZGaXQsdGVzdGluZyx0eXBlPSdwcm9iJykKICAgIHByZWRzcmYgPC0gaWZlbHNlKHByZWRzcmZwcm9icyRib3RuZXQgPj0wLjUsJ2JvdG5ldCcsJ25vcm1hbCcpCiAgICBjbSA8LSBjb25mdXNpb25NYXRyaXgocHJlZHNyZix0ZXN0aW5nJHN1YmNsYXNzKQogICAgbWV0cmljW2ldIDwtIGNtJGJ5Q2xhc3NbJ0YxJ10KICAgIAogICAgCiAgICAjVHJhaW5uaW5nIHByZWRpY3QKICAgIHByZWRzcmZwcm9ic190IDwtIHByZWRpY3QobmV3X3JmRml0LGF1eF90cmFpbmluZ19zZXQsdHlwZT0ncHJvYicpCiAgICBwcmVkc3JmX3QgPC0gaWZlbHNlKHByZWRzcmZwcm9ic190JGJvdG5ldCA+PSAwLjUsJ2JvdG5ldCcsJ25vcm1hbCcpCiAgICBjbV90IDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkc3JmX3QsYXV4X3RyYWluaW5nX3NldCRzdWJjbGFzcykKICAgIG1ldHJpY190W2ldIDwtIGNtX3QkYnlDbGFzc1snRjEnXQogICAgCiAgfQogIG91dHB1dCA8LSBkby5jYWxsKHJiaW5kLCBNYXAoZGF0YS5mcmFtZSwgZGF0YV9jb3VudD1jb3VudF9yYW5kb20sIG1ldHJpYz1tZXRyaWMpKQogIG91dHB1dF90IDwtIGRvLmNhbGwocmJpbmQsIE1hcChkYXRhLmZyYW1lLCBkYXRhX2NvdW50PWNvdW50X3JhbmRvbSwgbWV0cmljPW1ldHJpY190KSkKICBsaXN0X3Jlc3VsdCA8LSBsaXN0KCdvdXRwdXQnID0gb3V0cHV0LCAnb3V0cHV0X3QnID0gb3V0cHV0X3QsICd0ZXN0aW5nX3Jlc3VsdCcgPSB0ZXN0aW5nX3Jlc3VsdCkKfQoKZ2VuZXJhdGVfZGF0YV9ub2lzeSA8LSBmdW5jdGlvbihkYXRhc2V0LCBwb3JjZW50KXsKICBsaXN0X2F1eCA8LSBzYW1wbGUobnJvdyhkYXRhc2V0KSAscG9yY2VudCkKICBub2lzeV9kYXRhX3NhbXBsZSA8LSBkYXRhc2V0W2xpc3RfYXV4LF0KICBub19ub2lzeV9kYXRhX3NhbXBsZSA8LSBkYXRhc2V0Wy1saXN0X2F1eCxdCiAgCiAgbm9pc3lfZGF0YV9zYW1wbGVfYiA8LSBub2lzeV9kYXRhX3NhbXBsZSAlPiUgZmlsdGVyKGNsYXNzID09ICdCb3RuZXQnKQogIG5vaXN5X2RhdGFfc2FtcGxlX24gPC0gbm9pc3lfZGF0YV9zYW1wbGUgJT4lIGZpbHRlcihjbGFzcyA9PSAnTm9ybWFsJykKICAKICBub2lzeV9kYXRhX3NhbXBsZV9iJGNsYXNzIDwtIGFzLmNoYXJhY3Rlcihub2lzeV9kYXRhX3NhbXBsZV9iJGNsYXNzKQogIG5vaXN5X2RhdGFfc2FtcGxlX2IkY2xhc3Nbbm9pc3lfZGF0YV9zYW1wbGVfYiRjbGFzcyA9PSAnQm90bmV0J10gPC0gJ05vcm1hbCcKICBub2lzeV9kYXRhX3NhbXBsZV9iJGNsYXNzIDwtIGFzLmZhY3Rvcihub2lzeV9kYXRhX3NhbXBsZV9iJGNsYXNzKQogIAogIG5vaXN5X2RhdGFfc2FtcGxlX24kY2xhc3MgPC0gYXMuY2hhcmFjdGVyKG5vaXN5X2RhdGFfc2FtcGxlX24kY2xhc3MpCiAgbm9pc3lfZGF0YV9zYW1wbGVfbiRjbGFzc1tub2lzeV9kYXRhX3NhbXBsZV9uJGNsYXNzID09ICdOb3JtYWwnXSA8LSAnQm90bmV0JwogIG5vaXN5X2RhdGFfc2FtcGxlX24kY2xhc3MgPC0gYXMuZmFjdG9yKG5vaXN5X2RhdGFfc2FtcGxlX24kY2xhc3MpCiAgCiAgbm9pc3lfZGF0YSA8LSByYmluZChub2lzeV9kYXRhX3NhbXBsZV9iLCBub2lzeV9kYXRhX3NhbXBsZV9uKQogIHRyYWluaW5nX25vaXN5IDwtIHJiaW5kKG5vX25vaXN5X2RhdGFfc2FtcGxlLG5vaXN5X2RhdGEpCiAgdHJhaW5pbmdfbm9pc3kgPC0gdHJhaW5pbmdfbm9pc3lbc2FtcGxlKG5yb3codHJhaW5pbmdfbm9pc3kpLG5yb3codHJhaW5pbmdfbm9pc3kpKSxdCiAgcmV0dXJuKHRyYWluaW5nX25vaXN5KQp9CgpnZXRfRUxBX21lYXN1cmUgPC0gZnVuY3Rpb24oQTAsIEF4KXsKICBSTEEgPC0gYWJzKEEwIC0gQXgpIC8gQTAKICBGQTAgPC0gKDEgLSBBMCkgLyBBMAogIEVMQSA8LSBSTEEgKyBGQTAKICByZXR1cm4oRUxBKQp9CgpyYW5kb21Gb3Jlc3RfcGVyZm9ybWFjZSA8LSBmdW5jdGlvbih0cmFpbmluZ19kYXRhLCB0ZXN0aW5nX2RhdGEpewogIHJmRml0IDwtIHRyYWluKGNsYXNzIH4gc3Ard3Ard25wK3NucCtkcytkbStkbCtzcytzbStzbCwKICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5pbmdfZGF0YSwKICAgICAgICAgICAgICAgICBtZXRyaWM9IlJPQyIsCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJmIiwKICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBzZXR0aW5ncykKICBwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChyZkZpdCx0ZXN0aW5nX2RhdGEsdHlwZT0ncHJvYicpCiAgcHJlZHNyZiA8LSBpZmVsc2UocHJlZHNyZnByb2JzJEJvdG5ldCA+PTAuNSwnQm90bmV0JywnTm9ybWFsJykKICBjbSA8LSBjb25mdXNpb25NYXRyaXgocHJlZHNyZix0ZXN0aW5nX2RhdGEkY2xhc3MpCiAgcmVzdWx0IDwtIGNtJGJ5Q2xhc3MKICByZXR1cm4ocmVzdWx0KQp9Cgpjb3NpbmVfc2ltaWxhcml0eSA8LSBmdW5jdGlvbih2ZWN0b3IsIG1hdHJpeCl7CiAgcmVzdWx0IDwtIGMoKQogIGZvcihpIGluIGMoMTpucm93KG1hdHJpeCkpKXsKICAgIHYxIDwtIGFzLm51bWVyaWModmVjdG9yKQogICAgdjIgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IoYXMubWF0cml4KG1hdHJpeFtpLF0pKSkKICAgIHJlc3VsdFtpXSA9IHYxICUqJSB2MiAvIHNxcnQodjEgJSolIHYxICogdjIgJSolIHYyKQogIH0KICByZXR1cm4ocmVzdWx0KQp9CgpwcmVkaWN0aW9uX2J5X3NpbWlsYXJpdHkgPC0gZnVuY3Rpb24odHJhaW5fZWxlbWVudCwgcHJlZGljdGlvbl9lbGVtZW50LG51bWJlcl9lbGVtZW50KXsKICBudW1lcmljX3RyYWluX2VsZW1lbnQgPC0gdHJhaW5fZWxlbWVudFssMToxMF0gI0dldHRpbmcgY2hhcmFjdGVyaXN0aWMgbnVtZXJpYyB2ZWN0b3IKICBzaW1pbGFyaXR5X3Jlc3VsdCA8LSBjb3NpbmVfc2ltaWxhcml0eShwcmVkaWN0aW9uX2VsZW1lbnRbMToxMF0sbnVtZXJpY190cmFpbl9lbGVtZW50KQogIHRyYWluX2VsZW1lbnQkc2ltaWxhcml0eV9yZXN1bHQgPC0gc2ltaWxhcml0eV9yZXN1bHQKICB0cmFpbl9lbGVtZW50X29yZGVyX2J5X3NpbWlsYXJpdHkgPC0gdHJhaW5fZWxlbWVudFtvcmRlcih0cmFpbl9lbGVtZW50JHNpbWlsYXJpdHlfcmVzdWx0LCBkZWNyZWFzaW5nID0gVCksXQogIHRyYWluX2VsZW1lbnRfb3JkZXJfYnlfc2ltaWxhcml0eSA8LSB0cmFpbl9lbGVtZW50X29yZGVyX2J5X3NpbWlsYXJpdHlbMTpudW1iZXJfZWxlbWVudCxdCiAgYXV4IDwtIHRyYWluX2VsZW1lbnRfb3JkZXJfYnlfc2ltaWxhcml0eSAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2Uobj1uKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCiAgcmVzdWx0IDwtIGF1eFsxLDFdCiAgcmV0dXJuKHJlc3VsdCkKfQoKdHJhaW5pbmcKdGVzdGluZwpgYGAKCiMjIyBEYXRhIHRyYWluaW5nIHBhcnRpdGlvbnM6IGNvbGQgc3RhcnQgc3R1ZHkKIyMjIEl0ZXJhdGlvbiAjMQpgYGB7cn0Kc2V0LnNlZWQoMjAxKQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnRyYWluaW5nLnNhbXBsZWRfMSA8LSB0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgc2l6ZV90cmFpbmluZyksIF0KCnJlc3VsdCA8LSBjb2xkX3N0YXJ0X2RhdGEodHJhaW5pbmcuc2FtcGxlZF8xLCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKb3V0cHV0XzEgPC0gcmVzdWx0JG91dHB1dApvdXRwdXRfdF8xIDwtIHJlc3VsdCRvdXRwdXRfdApvdXRwdXRfMQoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfMSkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCmZpcnN0X3RyYWluaW5nX3NhbXBsZSA8LSB0cmFpbmluZy5zYW1wbGVkXzFbMToyMDAsXQpnZ3Bsb3QoZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhzdWJjbGFzcykpCmNsYXNzX2Rpc3RyaWJ1dGlvbiA8LSBmaXJzdF90cmFpbmluZ19zYW1wbGUgJT4lIGdyb3VwX2J5KGNsYXNzKSAlPiUgc3VtbWFyaXNlKG4gPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCmdncGxvdChmaXJzdF90cmFpbmluZ19zYW1wbGUpICsgZ2VvbV9iYXIoYWVzKGNsYXNzKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQoKdGVzdGluZ19yZXN1bHQgPC0gcmVzdWx0JHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfdF8xKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpvdXRwdXRfdF9hdXhfMSA8LSBvdXRwdXRfdF8xCm5hbWVzKG91dHB1dF90X2F1eF8xKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCm91dHB1dF9yZXN1bHRfMSA8LSBjYmluZChvdXRwdXRfMSxvdXRwdXRfdF9hdXhfMSkKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfcmVzdWx0XzEpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCiMjIyBJdGVyYXRpb24gIzIKYGBge3J9CnNldC5zZWVkKDIwMikKc2l6ZV90cmFpbmluZyA8LSBucm93KHRyYWluaW5nKQp0cmFpbmluZy5zYW1wbGVkXzIgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCgpyZXN1bHRfMiA8LSBjb2xkX3N0YXJ0X2RhdGEodHJhaW5pbmcuc2FtcGxlZF8yLCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKb3V0cHV0XzIgPC0gcmVzdWx0XzIkb3V0cHV0Cm91dHB1dF90XzIgPC0gcmVzdWx0XzIkb3V0cHV0X3QKb3V0cHV0XzIKCmdnIDwtIGdncGxvdChkYXRhID0gb3V0cHV0XzIpCiAgZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogICAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgICBjb2xvcj1OVUxMKQpmaXJzdF90cmFpbmluZ19zYW1wbGUgPC0gdHJhaW5pbmcuc2FtcGxlZF8yWzE6MjAwLF0KZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpjbGFzc19kaXN0cmlidXRpb24gPC0gZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QoZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCnRlc3RpbmdfcmVzdWx0XzIgPC0gcmVzdWx0JHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfdF8yKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpvdXRwdXRfdF9hdXhfMiA8LSBvdXRwdXRfdF8yCm5hbWVzKG91dHB1dF90X2F1eF8yKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCm91dHB1dF9yZXN1bHRfMiA8LSBjYmluZChvdXRwdXRfMixvdXRwdXRfdF9hdXhfMikKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfcmVzdWx0XzIpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICMzCmBgYHtyfQpzZXQuc2VlZCgyMzMpCnNpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKdHJhaW5pbmcuc2FtcGxlZF8zIDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQoKcmVzdWx0XzMgPC0gY29sZF9zdGFydF9kYXRhKHRyYWluaW5nLnNhbXBsZWRfMywgdGVzdGluZywgc2V0dGluZ3MgPSBjdHJsX2Zhc3QpCm91dHB1dF8zIDwtIHJlc3VsdF8zJG91dHB1dApvdXRwdXRfdF8zIDwtIHJlc3VsdF8zJG91dHB1dF90Cm91dHB1dF8zCgoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfMykKICBnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ3JlZCcpICsgCiAgICBsYWJzKHRpdGxlPSJSYW5kb20gRm9yZXN0IHRocm91Z2ggZGF0YSB0cmFpbmluZyBzaXplIiwgCiAgICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICAgIHk9IkYxIFNjb3JlIiwgCiAgICAgICAgIGNvbG9yPU5VTEwpCmZpcnN0X3RyYWluaW5nX3NhbXBsZSA8LSB0cmFpbmluZy5zYW1wbGVkXzNbMToyMDAsXQpnZ3Bsb3QoZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhzdWJjbGFzcykpCmNsYXNzX2Rpc3RyaWJ1dGlvbiA8LSBmaXJzdF90cmFpbmluZ19zYW1wbGUgJT4lIGdyb3VwX2J5KGNsYXNzKSAlPiUgc3VtbWFyaXNlKG4gPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MobikpCmdncGxvdChmaXJzdF90cmFpbmluZ19zYW1wbGUpICsgZ2VvbV9iYXIoYWVzKGNsYXNzKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQoKdGVzdGluZ19yZXN1bHRfMyA8LSByZXN1bHQkdGVzdGluZ19yZXN1bHQKI3Rlc3RpbmdfcmVzdWx0CgpnZyA8LSBnZ3Bsb3QoZGF0YSA9IG91dHB1dF90XzMpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKCm91dHB1dF90X2F1eF8zIDwtIG91dHB1dF90XzMKbmFtZXMob3V0cHV0X3RfYXV4XzMpIDwtIGMoJ2RhdGFfY291bnRfdCcsJ21ldHJpY190JykKb3V0cHV0X3Jlc3VsdF8zIDwtIGNiaW5kKG91dHB1dF8zLG91dHB1dF90X2F1eF8zKQpnZyA8LSBnZ3Bsb3QoZGF0YSA9IG91dHB1dF9yZXN1bHRfMykKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnRfdCwgeSA9IG1ldHJpY190KSxjb2xvciA9ICdibHVlJykgKyAKICBsYWJzKHRpdGxlPSJSYW5kb20gRm9yZXN0IHRocm91Z2ggZGF0YSB0cmFpbmluZyBzaXplIiwgCiAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgY2FwdGlvbj0iU291cmNlOiBDVFUtMTMiLCAKICAgICAgIHk9IkYxIFNjb3JlIiwgCiAgICAgICBjb2xvcj1OVUxMKQpgYGAKCiMjIyBJdGVyYXRpb24gIzQKYGBge3J9CnNldC5zZWVkKDIwNCkKc2l6ZV90cmFpbmluZyA8LSBucm93KHRyYWluaW5nKQp0cmFpbmluZy5zYW1wbGVkXzQgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCgpyZXN1bHRfNCA8LSBjb2xkX3N0YXJ0X2RhdGEodHJhaW5pbmcuc2FtcGxlZF80LCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKb3V0cHV0XzQgPC0gcmVzdWx0XzQkb3V0cHV0Cm91dHB1dF90XzQgPC0gcmVzdWx0XzQkb3V0cHV0X3QKb3V0cHV0XzQKCmdnIDwtIGdncGxvdChkYXRhID0gb3V0cHV0XzQpCiAgZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogICAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgICBjb2xvcj1OVUxMKQpmaXJzdF90cmFpbmluZ19zYW1wbGUgPC0gdHJhaW5pbmcuc2FtcGxlZF80WzE6MjAwLF0KZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpjbGFzc19kaXN0cmlidXRpb24gPC0gZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QoZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCnRlc3RpbmdfcmVzdWx0XzQgPC0gcmVzdWx0JHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfdF80KQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpvdXRwdXRfdF9hdXhfNCA8LSBvdXRwdXRfdF80Cm5hbWVzKG91dHB1dF90X2F1eF80KSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCm91dHB1dF9yZXN1bHRfNCA8LSBjYmluZChvdXRwdXRfNCxvdXRwdXRfdF9hdXhfNCkKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXRfcmVzdWx0XzQpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICM1CmBgYHtyfQpzZXQuc2VlZCgyMDUpCnNpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKdHJhaW5pbmcuc2FtcGxlZF81IDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQoKcmVzdWx0XzUgPC0gY29sZF9zdGFydF9kYXRhKHRyYWluaW5nLnNhbXBsZWRfNSwgdGVzdGluZywgc2V0dGluZ3MgPSBjdHJsX2Zhc3QpCm91dHB1dF81IDwtIHJlc3VsdF81JG91dHB1dApvdXRwdXRfdF81IDwtIHJlc3VsdF81JG91dHB1dF90Cm91dHB1dF81CgpnZyA8LSBnZ3Bsb3QoZGF0YSA9IG91dHB1dF81KQogIGdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyAKICAgIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBDVFUtMTMiLCAKICAgICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgICAgY29sb3I9TlVMTCkKZmlyc3RfdHJhaW5pbmdfc2FtcGxlIDwtIHRyYWluaW5nLnNhbXBsZWRfNVsxOjIwMCxdCmdncGxvdChmaXJzdF90cmFpbmluZ19zYW1wbGUpICsgZ2VvbV9iYXIoYWVzKHN1YmNsYXNzKSkKY2xhc3NfZGlzdHJpYnV0aW9uIDwtIGZpcnN0X3RyYWluaW5nX3NhbXBsZSAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2UobiA9IG4oKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoY2xhc3MpKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCgp0ZXN0aW5nX3Jlc3VsdF81IDwtIHJlc3VsdCR0ZXN0aW5nX3Jlc3VsdAojdGVzdGluZ19yZXN1bHQKCmdnIDwtIGdncGxvdChkYXRhID0gb3V0cHV0X3RfNSkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdibHVlJykgKyAKICBsYWJzKHRpdGxlPSJSYW5kb20gRm9yZXN0IHRocm91Z2ggZGF0YSB0cmFpbmluZyBzaXplIiwgCiAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgY2FwdGlvbj0iU291cmNlOiBDVFUtMTMiLCAKICAgICAgIHk9IkYxIFNjb3JlIiwgCiAgICAgICBjb2xvcj1OVUxMKQoKb3V0cHV0X3RfYXV4XzUgPC0gb3V0cHV0X3RfNQpuYW1lcyhvdXRwdXRfdF9hdXhfNSkgPC0gYygnZGF0YV9jb3VudF90JywnbWV0cmljX3QnKQpvdXRwdXRfcmVzdWx0XzUgPC0gY2JpbmQob3V0cHV0XzUsb3V0cHV0X3RfYXV4XzUpCmdnIDwtIGdncGxvdChkYXRhID0gb3V0cHV0X3Jlc3VsdF81KQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ3JlZCcpICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudF90LCB5ID0gbWV0cmljX3QpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCmBgYAoKIyMjIERhdGEgdHJhaW5pbmcgcGFydGl0aW9uczogY29sZCBzdGFydCBzdHVkeSAoc2ltcGxlIFJhbmRvbSBGb3Jlc3QpCmBgYHtyfQpzZXQuc2VlZCgyMTEpCnNpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKcmZfdHJhaW5pbmcuc2FtcGxlZF8xIDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQpyZl9yZXN1bHRfMSA8LSBjb2xkX3N0YXJ0X2RhdGFfb25seV9yZihyZl90cmFpbmluZy5zYW1wbGVkXzEsIHRlc3RpbmcsIHNldHRpbmdzID0gY3RybF9mYXN0KQoKc2V0LnNlZWQoMjIyKQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnJmX3RyYWluaW5nLnNhbXBsZWRfMiA8LSB0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgc2l6ZV90cmFpbmluZyksIF0KcmZfcmVzdWx0XzIgPC0gY29sZF9zdGFydF9kYXRhX29ubHlfcmYocmZfdHJhaW5pbmcuc2FtcGxlZF8yLCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKCnNldC5zZWVkKDIyMykKc2l6ZV90cmFpbmluZyA8LSBucm93KHRyYWluaW5nKQpyZl90cmFpbmluZy5zYW1wbGVkXzMgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCnJmX3Jlc3VsdF8zIDwtIGNvbGRfc3RhcnRfZGF0YV9vbmx5X3JmKHJmX3RyYWluaW5nLnNhbXBsZWRfMywgdGVzdGluZywgc2V0dGluZ3MgPSBjdHJsX2Zhc3QpCgpzZXQuc2VlZCgyMjQpCnNpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKcmZfdHJhaW5pbmcuc2FtcGxlZF80IDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQpyZl9yZXN1bHRfNCA8LSBjb2xkX3N0YXJ0X2RhdGFfb25seV9yZihyZl90cmFpbmluZy5zYW1wbGVkXzQsIHRlc3RpbmcsIHNldHRpbmdzID0gY3RybF9mYXN0KQoKc2V0LnNlZWQoMjI1KQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnJmX3RyYWluaW5nLnNhbXBsZWRfNSA8LSB0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgc2l6ZV90cmFpbmluZyksIF0KcmZfcmVzdWx0XzUgPC0gY29sZF9zdGFydF9kYXRhX29ubHlfcmYocmZfdHJhaW5pbmcuc2FtcGxlZF81LCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKCnJmX2RhdGEucmVzdWx0IDwtIGRhdGEuZnJhbWUocmZfcmVzdWx0XzUkb3V0cHV0JGRhdGFfY291bnQpCnJmX2RhdGEucmVzdWx0X3QgPC0gZGF0YS5mcmFtZShyZl9yZXN1bHRfNSRvdXRwdXQkZGF0YV9jb3VudCkKZm9yKGkgaW4gYygxOjMwKSl7CiAgY3VycmVudF9zZWVkIDwtIDIyNiArIGkKICBzZXQuc2VlZChjdXJyZW50X3NlZWQpCiAgI3NpemVfdHJhaW5pbmcgPC0gbnJvdyh0cmFpbmluZykKICByZl90cmFpbmluZy5zYW1wbGVkX2N1cnJlbnQgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCiAgcmZfcmVzdWx0X2N1cnJlbnQgPC0gY29sZF9zdGFydF9kYXRhX29ubHlfcmYocmZfdHJhaW5pbmcuc2FtcGxlZF9jdXJyZW50LCB0ZXN0aW5nLCBzZXR0aW5ncyA9IGN0cmxfZmFzdCkKICAKICByZl9kYXRhLnJlc3VsdCA8LSBjYmluZChyZl9kYXRhLnJlc3VsdCxyZl9yZXN1bHRfY3VycmVudCRvdXRwdXQkbWV0cmljKQogIHJmX2RhdGEucmVzdWx0X3QgPC0gY2JpbmQocmZfZGF0YS5yZXN1bHRfdCxyZl9yZXN1bHRfY3VycmVudCRvdXRwdXRfdCRtZXRyaWMpCn0KIAogeCA8LSBjKCdjb3VudF9vZl9kYXRhJykKIGZvcihpIGluIGMoMTozMCkpewogICB4W2krMV0gPC0gcGFzdGUoJ2l0ZXJhdGlvbl8nLHRvU3RyaW5nKGkpLHNlcCA9ICIiKQogfQogeAogbmFtZXMocmZfZGF0YS5yZXN1bHQpIDwtIHgKIG5hbWVzKHJmX2RhdGEucmVzdWx0X3QpIDwtIHgKIHJmX2RhdGEucmVzdWx0CiByZl9kYXRhLnJlc3VsdF90IAogCiAjd3JpdGUudGFibGUocmZfZGF0YS5yZXN1bHQsZmlsZT0icmFuZG9tX2ZvcmVzdF8zMF9pdGVyYXRpb25zX2YxX3Rlc3RpbmcudHh0IixzZXA9InwiLCByb3cubmFtZXMgPSBGKQogI3dyaXRlLnRhYmxlKHJmX2RhdGEucmVzdWx0X3QsZmlsZT0icmFuZG9tX2ZvcmVzdF8zMF9pdGVyYXRpb25zX2YxX3RyYWluaW5nLnR4dCIsc2VwPSJ8Iiwgcm93Lm5hbWVzID0gRikKYGBgCgojIyMgSXRlcmF0aW9uICMxCmBgYHtyfQpyZl9vdXRwdXRfMSA8LSByZl9yZXN1bHRfMSRvdXRwdXQKcmZfb3V0cHV0X3RfMSA8LSByZl9yZXN1bHRfMSRvdXRwdXRfdApyZl9vdXRwdXRfMQoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfMSkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF8xWzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8xIDwtIHJmX3Jlc3VsdF8xJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF8xKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfMSA8LSByZl9vdXRwdXRfdF8xCm5hbWVzKHJmX291dHB1dF90X2F1eF8xKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfMSA8LSBjYmluZChyZl9vdXRwdXRfMSxyZl9vdXRwdXRfdF9hdXhfMSkKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzEpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICMyCmBgYHtyfQpyZl9vdXRwdXRfMiA8LSByZl9yZXN1bHRfMiRvdXRwdXQKcmZfb3V0cHV0X3RfMiA8LSByZl9yZXN1bHRfMiRvdXRwdXRfdApyZl9vdXRwdXRfMgoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfMikKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF8yWzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8yIDwtIHJmX3Jlc3VsdF8yJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF8yKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfMiA8LSByZl9vdXRwdXRfdF8yCm5hbWVzKHJmX291dHB1dF90X2F1eF8yKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfMiA8LSBjYmluZChyZl9vdXRwdXRfMixyZl9vdXRwdXRfdF9hdXhfMikKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzIpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICMzCmBgYHtyfQpyZl9vdXRwdXRfMyA8LSByZl9yZXN1bHRfMyRvdXRwdXQKcmZfb3V0cHV0X3RfMyA8LSByZl9yZXN1bHRfMyRvdXRwdXRfdApyZl9vdXRwdXRfMwoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfMykKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF8zWzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8yIDwtIHJmX3Jlc3VsdF8yJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF8zKQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfMyA8LSByZl9vdXRwdXRfdF8zCm5hbWVzKHJmX291dHB1dF90X2F1eF8zKSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfMyA8LSBjYmluZChyZl9vdXRwdXRfMyxyZl9vdXRwdXRfdF9hdXhfMykKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzMpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICM0CmBgYHtyfQpyZl9vdXRwdXRfNCA8LSByZl9yZXN1bHRfNCRvdXRwdXQKcmZfb3V0cHV0X3RfNCA8LSByZl9yZXN1bHRfNCRvdXRwdXRfdApyZl9vdXRwdXRfNAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfNCkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF80WzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8yIDwtIHJmX3Jlc3VsdF8yJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF80KQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfNCA8LSByZl9vdXRwdXRfdF80Cm5hbWVzKHJmX291dHB1dF90X2F1eF80KSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfNCA8LSBjYmluZChyZl9vdXRwdXRfNCxyZl9vdXRwdXRfdF9hdXhfNCkKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzQpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgojIyMgSXRlcmF0aW9uICM1CmBgYHtyfQpyZl9vdXRwdXRfNSA8LSByZl9yZXN1bHRfNSRvdXRwdXQKcmZfb3V0cHV0X3RfNSA8LSByZl9yZXN1bHRfNSRvdXRwdXRfdApyZl9vdXRwdXRfNQoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfNSkKZ2cgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50LCB5ID0gbWV0cmljKSxjb2xvciA9ICdyZWQnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9maXJzdF90cmFpbmluZ19zYW1wbGUgPC0gcmZfdHJhaW5pbmcuc2FtcGxlZF81WzE6MjAwLF0KZ2dwbG90KHJmX2ZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQpyZl9jbGFzc19kaXN0cmlidXRpb24gPC0gcmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpnZ3Bsb3QocmZfZmlyc3RfdHJhaW5pbmdfc2FtcGxlKSArIGdlb21fYmFyKGFlcyhjbGFzcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCiNyZl90ZXN0aW5nX3Jlc3VsdF8yIDwtIHJmX3Jlc3VsdF8yJHRlc3RpbmdfcmVzdWx0CiN0ZXN0aW5nX3Jlc3VsdAoKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfdF81KQpnZyArIGdlb21fbGluZShhZXMoeCA9IGRhdGFfY291bnQsIHkgPSBtZXRyaWMpLGNvbG9yID0gJ2JsdWUnKSArIAogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3QgdGhyb3VnaCBkYXRhIHRyYWluaW5nIHNpemUiLCAKICAgICAgICNzdWJ0aXRsZT0iRHJhd24gZnJvbSBMb25nIERhdGEgZm9ybWF0IiwgCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IENUVS0xMyIsIAogICAgICAgeT0iRjEgU2NvcmUiLCAKICAgICAgIGNvbG9yPU5VTEwpCgpyZl9vdXRwdXRfdF9hdXhfNSA8LSByZl9vdXRwdXRfdF81Cm5hbWVzKHJmX291dHB1dF90X2F1eF81KSA8LSBjKCdkYXRhX2NvdW50X3QnLCdtZXRyaWNfdCcpCnJmX291dHB1dF9yZXN1bHRfNSA8LSBjYmluZChyZl9vdXRwdXRfNSxyZl9vdXRwdXRfdF9hdXhfNSkKZ2cgPC0gZ2dwbG90KGRhdGEgPSByZl9vdXRwdXRfcmVzdWx0XzUpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyBnZW9tX2xpbmUoYWVzKHggPSBkYXRhX2NvdW50X3QsIHkgPSBtZXRyaWNfdCksY29sb3IgPSAnYmx1ZScpICsgCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdCB0aHJvdWdoIGRhdGEgdHJhaW5pbmcgc2l6ZSIsIAogICAgICAgI3N1YnRpdGxlPSJEcmF3biBmcm9tIExvbmcgRGF0YSBmb3JtYXQiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogQ1RVLTEzIiwgCiAgICAgICB5PSJGMSBTY29yZSIsIAogICAgICAgY29sb3I9TlVMTCkKYGBgCgoKIyMjIFN0dWRpZXMgU2FtcGxlcwpgYGB7cn0KZmlyc3RfdHJhaW5pbmdfc2FtcGxlIDwtIHRyYWluaW5nLnNhbXBsZWRbMToyMDAsXQpmaXJzdF90cmFpbmluZ19zYW1wbGUKZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoc3ViY2xhc3MpKQoKY2xhc3NfZGlzdHJpYnV0aW9uIDwtIGZpcnN0X3RyYWluaW5nX3NhbXBsZSAlPiUgZ3JvdXBfYnkoY2xhc3MpICU+JSBzdW1tYXJpc2UobiA9IG4oKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKZ2dwbG90KGZpcnN0X3RyYWluaW5nX3NhbXBsZSkgKyBnZW9tX2JhcihhZXMoY2xhc3MpKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCgpgYGAKCgojIyMgcGFydCAyCmBgYHtyfQpzZXQuc2VlZCgyMDYpCmxpYnJhcnkoZG9QYXJhbGxlbCkKY2wgPC0gbWFrZUNsdXN0ZXIoMikKcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnNwbGl0X3NpemVfdHJhaW5pbmcgPSBzaXplX3RyYWluaW5nIC8gMjAwCmNvdW50X3JhbmRvbSA8LSBmb3JlYWNoKGk9MTpzcGxpdF9zaXplX3RyYWluaW5nKSAlZG9wYXIlIHsKICAyMDAgKiBpCn0KdHJhaW5pbmcuc2FtcGxlZCA8LSB0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgc2l6ZV90cmFpbmluZyksIF0KbWV0cmljIDwtIGZvcmVhY2goaT0xOnNwbGl0X3NpemVfdHJhaW5pbmcpICVkbyUgewogICNsaWJyYXJ5KGNhcmV0KQogIGNvdW50IDwtIDIwMCAqIGkKICBhdXhfdHJhaW5pbmdfc2V0IDwtIHRyYWluaW5nLnNhbXBsZWRbYygxOmNvdW50KSwgXSN0cmFpbmluZ1tzYW1wbGUoc2l6ZV90cmFpbmluZywgY291bnQpLCBdCiAgY2x1c3RlcnMgPC0ga21lYW5zKGF1eF90cmFpbmluZ19zZXRbLC1jKDExLDEyLDEzLDE0KV0sMyxuc3RhcnQgPSAyNSkKICBhdXhfdHJhaW5pbmdfc2V0X2NsdXN0ZXIgPC0gY2JpbmQoYXV4X3RyYWluaW5nX3NldCwgY2x1c3RlciA9IGNsdXN0ZXJzJGNsdXN0ZXIpCiAgcmVzdWx0X3ZlY3RvciA8LSBudW1lcmljKG5yb3codGVzdGluZykpCiAgZm9yIChqIGluIGMoMTozKSl7CiAgICBjbHVzdGVyX2RhdGEgPC0gZmlsdGVyKGF1eF90cmFpbmluZ19zZXRfY2x1c3RlciwgY2x1c3RlciA9PSBqKQogICAgbmV3X3JmRml0IDwtIHRyYWluKGNsYXNzIH4gc3Ard3Ard25wK3NucCtkcytkbStkbCtzcytzbStzbCwKICAgICAgICAgICAgICAgZGF0YSA9IGNsdXN0ZXJfZGF0YSwKICAgICAgICAgICAgICAgbWV0cmljPSJST0MiLAogICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsX2Zhc3QpCiAgICBwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChuZXdfcmZGaXQsdGVzdGluZyx0eXBlPSdwcm9iJykKICAgIGZvciAoayBpbiBjKDE6bGVuZ3RoKHJlc3VsdF92ZWN0b3IpKSl7CiAgICAgIGlmKHByZWRzcmZwcm9icyRCb3RuZXRba10gPiAwLjUpewogICAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSArIDEKICAgICAgfQogICAgICBlbHNlewogICAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSAtIDEKICAgICAgfQogICAgfQogICAgCiAgfQogIGEgPSBpZmVsc2UocmVzdWx0X3ZlY3RvciA+IDAsJ0JvdG5ldCcsJ05vcm1hbCcpCiAgY20gPC0gY29uZnVzaW9uTWF0cml4KGEsdGVzdGluZyRjbGFzcykKICBtZXRyaWMgPC0gY20kYnlDbGFzc1snRjEnXSNjbSRvdmVyYWxsWzFdCiAgbWV0cmljCiAgCn0KCm91dHB1dCA8LSBkby5jYWxsKHJiaW5kLCBNYXAoZGF0YS5mcmFtZSwgZGF0YV9jb3VudD1jb3VudF9yYW5kb20sIG1ldHJpYz1tZXRyaWMpKQpvdXRwdXQKZ2cgPC0gZ2dwbG90KGRhdGEgPSBvdXRwdXQpCmdnICsgZ2VvbV9saW5lKGFlcyh4ID0gZGF0YV9jb3VudCwgeSA9IG1ldHJpYyksY29sb3IgPSAncmVkJykgKyAKICBsYWJzKHRpdGxlPSJSYW5kb20gRm9yZXN0IHRocm91Z2ggZGF0YSB0cmFpbmluZyBzaXplIiwgCiAgICAgICAjc3VidGl0bGU9IkRyYXduIGZyb20gTG9uZyBEYXRhIGZvcm1hdCIsIAogICAgICAgY2FwdGlvbj0iU291cmNlOiBDVFUtMTMiLCAKICAgICAgIHk9IkFjY3VyYWN5IiwgCiAgICAgICBjb2xvcj1OVUxMKQpjbHVzdGVyX2RhdGEKYGBgCgoKIyMjIFRlc3Qgd2l0aCBvbmx5IG9uZQpgYGB7cn0Kc2V0LnNlZWQoMjI2KQpzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCnRyYWluaW5nLnNhbXBsZWQgPC0gdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIHNpemVfdHJhaW5pbmcpLCBdCgphdXhfdHJhaW5pbmdfc2V0IDwtIHRyYWluaW5nLnNhbXBsZWRbYygxOjIwMCksIF0jdHJhaW5pbmdbc2FtcGxlKHNpemVfdHJhaW5pbmcsIDIwMCksIF0KY2x1c3RlcnMgPC0ga21lYW5zKGF1eF90cmFpbmluZ19zZXRbLC1jKDExLDEyLDEzLDE0KV0sMyxuc3RhcnQgPSAyNSkKYXV4X3RyYWluaW5nX3NldF9jbHVzdGVyIDwtIGNiaW5kKGF1eF90cmFpbmluZ19zZXQsIGNsdXN0ZXIgPSBjbHVzdGVycyRjbHVzdGVyKQpyZXN1bHRfdmVjdG9yIDwtIG51bWVyaWMobnJvdyh0ZXN0aW5nKSkKcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmcgPC0gbnVtZXJpYyhucm93KGF1eF90cmFpbmluZ19zZXQpKQoKZm9yIChqIGluIGMoMTozKSl7CiAgY2x1c3Rlcl9kYXRhIDwtIGF1eF90cmFpbmluZ19zZXRfY2x1c3RlciAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gaikKICBuZXdfcmZGaXQgPC0gdHJhaW4oc3ViY2xhc3MgfiBzcCt3cCt3bnArc25wK2RzK2RtK2RsK3NzK3NtK3NsLAogICAgICAgICAgICAgICBkYXRhID0gY2x1c3Rlcl9kYXRhLAogICAgICAgICAgICAgICBtZXRyaWM9IlJPQyIsCiAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsCiAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmxfZmFzdCkKICBwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChuZXdfcmZGaXQsdGVzdGluZyx0eXBlPSdwcm9iJykKICBmb3IgKGsgaW4gYygxOmxlbmd0aChyZXN1bHRfdmVjdG9yKSkpewogICAgaWYocHJlZHNyZnByb2JzJGJvdG5ldFtrXSA+IDAuNSl7CiAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSArIDEKICAgIH0KICAgIGVsc2V7CiAgICAgIHJlc3VsdF92ZWN0b3Jba10gPC0gcmVzdWx0X3ZlY3RvcltrXSAtIDEKICAgIH0KICB9CiAgCiAgI1RyYWlubmluZyBwcmVkaWN0CiAgcHJlZHNyZnByb2JzX3QgPC0gcHJlZGljdChuZXdfcmZGaXQsYXV4X3RyYWluaW5nX3NldCx0eXBlPSdwcm9iJykKICBmb3IgKGsgaW4gYygxOmxlbmd0aChyZXN1bHRfdmVjdG9yX3RyYWlubmluZykpKXsKICAgIGlmKHByZWRzcmZwcm9ic190JGJvdG5ldFtrXSA+IDAuNSl7CiAgICAgIHJlc3VsdF92ZWN0b3JfdHJhaW5uaW5nW2tdIDwtIHJlc3VsdF92ZWN0b3JfdHJhaW5uaW5nW2tdICsgMQogICAgfQogICAgZWxzZXsKICAgICAgcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmdba10gPC0gcmVzdWx0X3ZlY3Rvcl90cmFpbm5pbmdba10gLSAxCiAgICB9CiAgfQp9CgphID0gaWZlbHNlKHJlc3VsdF92ZWN0b3IgPiAwLCdib3RuZXQnLCdub3JtYWwnKQpiIDwtIGlmZWxzZShyZXN1bHRfdmVjdG9yX3RyYWlubmluZyA+IDAsJ2JvdG5ldCcsJ25vcm1hbCcpCmNtIDwtIGNvbmZ1c2lvbk1hdHJpeChhLHRlc3Rpbmckc3ViY2xhc3MpCm1ldHJpYyA8LSBjbSRieUNsYXNzWydGMSddI2NtJG92ZXJhbGxbMV0KbWV0cmljCmNtX3QgPC0gY29uZnVzaW9uTWF0cml4KGIsYXV4X3RyYWluaW5nX3NldCRzdWJjbGFzcykKbWV0cmljX3QgPC0gY21fdCRieUNsYXNzWydGMSddCm1ldHJpY190CmBgYAoKIyMjIFNhbXBsZSBleGFtcGxlcwpgYGB7cn0Kc2V0LnNlZWQoNTU2KQphID0gYygxLDIsMyw0LDUsNiw3LDgsOSkKciA8LSBzYW1wbGUoOSwzKQphW3JdCnIyIDwtIHNhbXBsZSg5LDMpCmFbcjJdCmBgYAoKYGBge3J9CiN0ZXN0aW5nX3Jlc3VsdAp0ZXN0aW5nX3Jlc3VsdC5ia3AgPC0gdGVzdGluZ19yZXN1bHQKdGVzdGluZ19yZXN1bHQKbmFtZXNfYXV4IDwtIGZvcmVhY2goaT0xOihucm93KHRyYWluaW5nKS8yMDApKSAlZG8lIHsKICAgIGl0ZXJhdGlvbiA8LSAyMDAgKiBpCiAgICBwYXN0ZSgnc2l6ZV8nLHRvU3RyaW5nKGl0ZXJhdGlvbiksc2VwID0gIiIpCn0KdGVzdGluZ19yZXN1bHRfbmFtZXMgPC0gdW5saXN0KG5hbWVzX2F1eCwgdXNlLm5hbWVzPUZBTFNFKQp0ZXN0aW5nX3Jlc3VsdCA8LSB0ZXN0aW5nX3Jlc3VsdFssYygtMSldCm5hbWVzKHRlc3RpbmdfcmVzdWx0KSA8LSB0ZXN0aW5nX3Jlc3VsdF9uYW1lcwp0ZXN0aW5nX3Jlc3VsdAoKdGVzdGluZ19hdXggPC0gY2JpbmQodGVzdGluZyx0ZXN0aW5nX3Jlc3VsdCkKdGVzdGluZ19hdXguYmtwMiA8LSB0ZXN0aW5nX2F1eAojd3JpdGUudGFibGUodGVzdGluZ19hdXgsZmlsZT0idGVzdGluZ19jbHVzdGVyX3Jlc3VsdC50eHQiLHNlcD0ifCIsIHJvdy5uYW1lcyA9IEYpCnRlc3RpbmdfYXV4CnN1bXMgPC0gcm93U3Vtcyh0ZXN0aW5nX2F1eFssLWMoMToxNCldKQpzdW1zCnRlc3RpbmdfYXV4WywtYygxOjE0KV0KdGVzdGluZ19hdXggPC0gY2JpbmQodGVzdGluZ19hdXgsc3VtcykKdGVzdGluZ19hdXgKdGVzdGluZ19hdXhfcmVzdWx0IDwtIHRlc3RpbmdfYXV4ICU+JSBncm91cF9ieShjbGFzcykgJT4lIHN1bW1hcmlzZShuID0gbigpLCBzdW1zID0gc3VtKHN1bXMpKSAlPiUgYXJyYW5nZShkZXNjKHN1bXMpKQp0ZXN0aW5nX2F1eF9yZXN1bHQKCmdyYXBoX3Rlc3RpbmdfcmVzdWx0IDwtIGdncGxvdCh0ZXN0aW5nX2F1eF9yZXN1bHRbLWMoMSxucm93KHRlc3RpbmdfYXV4X3Jlc3VsdCkpLF0pCmdyYXBoX3Rlc3RpbmdfcmVzdWx0ICsgZ2VvbV9wb2ludChhZXMoY2xhc3Msc3VtcykpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCmZlYXR1cmVfdmVjdG9yc19jbGVhbmVkCgpsaWJyYXJ5KGdyaWRFeHRyYSkKcGRmKCJkYXRhX291dHB1dC5wZGYiLCBoZWlnaHQ9MTEsIHdpZHRoPTguNSkKZ3JpZC50YWJsZShmZWF0dXJlX3ZlY3RvcnNfY2xlYW5lZFsxOjIwLF0pCmRldi5vZmYoKQoKdGVzdGluZ19yZXN1bHQuYmtwCnRlc3RpbmdfYXV4LmJrcDIKdGVzdGluZ19hdXhfcmVzdWx0CgpydXN0eV9kYXRhX3Jlc3VsdCA8LSB0ZXN0aW5nX2F1eC5ia3AyCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0IDwtIHJ1c3R5X2RhdGFfcmVzdWx0WywtYygxOjExLDE0KV0KcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRbLC1jKDEsMildCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0JHBvcyA8LSByb3dTdW1zKHJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0WywtYygxLDIpXSA+IDApCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0JG5lZyA8LSByb3dTdW1zKHJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0WywtYygxLDIpXSA8IDApCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0X2NsZWFuZWQgPC0gcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRbLGMoMSwyLDQ2LDQ3KV0KcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRfY2xlYW5lZApydXN0eV9kYXRhX3Jlc3VsdF9zaG9ydF9jbGVhbmVkX3Jlc3VsdCA8LSBydXN0eV9kYXRhX3Jlc3VsdF9zaG9ydF9jbGVhbmVkICU+JSBtdXRhdGUoZ29vZCA9IGlmZWxzZShzdWJjbGFzcyA9PSAnbm9ybWFsJyxuZWcscG9zKSkKcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRfY2xlYW5lZF9yZXN1bHQgPC0gcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRfY2xlYW5lZF9yZXN1bHQgJT4lIG11dGF0ZShiYWQgPSBpZmVsc2Uoc3ViY2xhc3MgPT0gJ25vcm1hbCcscG9zLG5lZykpCnJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0X2NsZWFuZWRfcmVzdWx0ICU+JSBncm91cF9ieShwb3J0KSAlPiUgc3VtbWFyaXNlKG49bigpLGdvb2QgPSBzdW0oZ29vZCksYmFkID0gc3VtKGJhZCkpICU+JSBhcnJhbmdlKGRlc2MobikpCgpkYXRhX2JvdG5ldF9wb3J0IDwtIHJ1c3R5X2RhdGFfcmVzdWx0X3Nob3J0X2NsZWFuZWRfcmVzdWx0ICU+JSBmaWx0ZXIoc3ViY2xhc3MgPT0gJ2JvdG5ldCcpCmRhdGFfbm9ybWFsX3BvcnQgPC0gcnVzdHlfZGF0YV9yZXN1bHRfc2hvcnRfY2xlYW5lZF9yZXN1bHQgJT4lIGZpbHRlcihzdWJjbGFzcyA9PSAnbm9ybWFsJykKZGF0YV9ib3RuZXRfcG9ydF9yZXN1bHQgPC0gIGRhdGFfYm90bmV0X3BvcnQgJT4lIGdyb3VwX2J5KHBvcnQpICU+JSBzdW1tYXJpc2Uobj1uKCksZ29vZCA9IHN1bShnb29kKSxiYWQgPSBzdW0oYmFkKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKZGF0YV9ub3JtYWxfcG9ydF9yZXN1bHQgPC0gZGF0YV9ub3JtYWxfcG9ydCAlPiUgZ3JvdXBfYnkocG9ydCkgJT4lIHN1bW1hcmlzZShuPW4oKSxnb29kID0gc3VtKGdvb2QpLGJhZCA9IHN1bShiYWQpKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpkYXRhX2JvdG5ldF9wb3J0X3Jlc3VsdApkYXRhX25vcm1hbF9wb3J0X3Jlc3VsdAoKZ2dwbG90KGRhdGEgPSBkYXRhX2JvdG5ldF9wb3J0X3Jlc3VsdCkgKyAKICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBwb3J0LCBmaWxsID0gY2xhcml0eSkpCgojd3JpdGUudGFibGUoZGF0YV9ib3RuZXRfcG9ydF9yZXN1bHQsZmlsZT0iZGF0YV9ib3RuZXRfcG9ydC50eHQiLHNlcD0ifCIsIHJvdy5uYW1lcyA9IEYpCmxpYnJhcnkocmVzaGFwZTIpCmRhdGEgPC0gZGF0YV9ib3RuZXRfcG9ydF9yZXN1bHQKZGF0YSRwb3J0IDwtIGFzLmZhY3RvcihkYXRhJHBvcnQpCgptZWx0KGRhdGFbLGMoMSwzLDQpXSkKCmdncGxvdChtZWx0KGRhdGFbLGMoMSwzLDQpXSkpKwogIGdlb21fY29sKGFlcyh4PXBvcnQseT12YWx1ZSxmaWxsPXZhcmlhYmxlKSkrCiAgI3RoZW1lX2J3KCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCgojTWFraW5nIE5vaXN5IGRhdGEgKHRyYWluaW5nX25vaXN5OiBkYXRhc2V0IHRvIHRyYWluIHdpdGggMjAlIG9mIG5vaXN5KQpgYGB7cn0Kc2V0LnNlZWQoMTAxKSAKdHJhaW5pbmcuYmtwIDwtIHRyYWluaW5nCm5vaXN5X2RhdGEgPC0gdHJhaW5pbmcKCnBvcmNlbnQgPC0gbnJvdyh0cmFpbmluZykgLyA1CnRyYWluaW5nX25vaXN5IDwtIGdlbmVyYXRlX2RhdGFfbm9pc3kobm9pc3lfZGF0YSxwb3JjZW50KQpucm93KHRyYWluaW5nKQpucm93KHRyYWluaW5nX25vaXN5KQpgYGAKCmBgYHtyfQpyZkZpdCA8LSB0cmFpbihjbGFzcyB+IHNwK3dwK3ducCtzbnArZHMrZG0rZGwrc3Mrc20rc2wsCiAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nLAogICAgICAgICAgICAgICAgIG1ldHJpYz0iUk9DIiwKICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHNldHRpbmdzKQpwcmVkc3JmcHJvYnMgPC0gcHJlZGljdChyZkZpdCx0ZXN0aW5nLHR5cGU9J3Byb2InKQpwcmVkc3JmIDwtIGlmZWxzZShwcmVkc3JmcHJvYnMkQm90bmV0ID49MC41LCdCb3RuZXQnLCdOb3JtYWwnKQpjbSA8LSBjb25mdXNpb25NYXRyaXgocHJlZHNyZix0ZXN0aW5nJGNsYXNzKQpyZXN1bHQgPC0gY20kYnlDbGFzc1sxMV0KY20kb3ZlcmFsbFsxXQpgYGAKCiNSb2J1c3RuZXNzIEFuYWxpc3lzKG9uZSBzaW1wbGUgaXRlcmF0aW9uKQpgYGB7cn0Kc2V0LnNlZWQoMzIxKQpwYXJ0aXRpb25zIDwtIHNlcSgyLDkwLDIpCnJmX21lYXN1cmVzX3Jlc3VsdCA8LSBjKCkKZWxhX21lYXN1cmVzX3Jlc3VsdCA8LSBjKCkKdG90YWwgPC0gbnJvdyh0cmFpbmluZykKYmFsYW5jZWRfYWNjdXJhY3kgPC0gcmFuZG9tRm9yZXN0X3BlcmZvcm1hY2UodHJhaW5pbmcsdGVzdGluZykKCmZvcihpIGluIHBhcnRpdGlvbnMpewogIHBvcmNlbnQgPC0gKGkqdG90YWwpIC8gMTAwCiAgdHJhaW5pbmdfbm9pc3kgPC0gZ2VuZXJhdGVfZGF0YV9ub2lzeShub2lzeV9kYXRhLHBvcmNlbnQpCiAgYmFsYW5jZWRfYWNjdXJhY3lfYXV4IDwtIHJhbmRvbUZvcmVzdF9wZXJmb3JtYWNlKHRyYWluaW5nX25vaXN5LHRlc3RpbmcpCiAgcmZfbWVhc3VyZXNfcmVzdWx0W2ldIDwtIGJhbGFuY2VkX2FjY3VyYWN5X2F1eAp9Cgpmb3IoaSBpbiBwYXJ0aXRpb25zKXsKICBiYWxhbmNlZF9hY2N1cmFjeV9ub2lzeSA8LSByZl9tZWFzdXJlc19yZXN1bHRbaV0KICBlbGFfbWVhc3VyZXNfcmVzdWx0W2ldIDwtIGdldF9FTEFfbWVhc3VyZShBMCA9IGJhbGFuY2VkX2FjY3VyYWN5WydCYWxhbmNlZCBBY2N1cmFjeSddLCBBeCA9IGJhbGFuY2VkX2FjY3VyYWN5X25vaXN5KQp9CgpwbG90KGVsYV9tZWFzdXJlc19yZXN1bHQpCiBwbG90KHJmX21lYXN1cmVzX3Jlc3VsdCkKYGBgCgojUm9idXN0bmVzcyBBbmFsaXN5czogcGxvdGluZyBhY2N1cmFjeQpgYGB7cn0KaW5kZXggPC0gc2VxKDIsOTAsMikKbWVhc3VyZV9yZXN1bHQgPC0gcmZfbWVhc3VyZXNfcmVzdWx0W2luZGV4XQpybGFfbWVhc3VyZV9kYXRhIDwtIGRhdGEuZnJhbWUoaW5kZXgsbWVhc3VyZV9yZXN1bHQpCm5hbWVzKHJsYV9tZWFzdXJlX2RhdGEpIDwtIGMoJ25vaXNlX3BvcmNlbnQnLCdiYWxhbmNlZF9hY2N1cmFjeScpCgpnPC1nZ3Bsb3QocmxhX21lYXN1cmVfZGF0YSkgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IG5vaXNlX3BvcmNlbnQsIHkgPSBiYWxhbmNlZF9hY2N1cmFjeSkpICsgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gbm9pc2VfcG9yY2VudCwgeSA9IGJhbGFuY2VkX2FjY3VyYWN5KSkKZ2dwbG90bHkoZykKYGBgCgojUm9idXN0bmVzcyBBbmFsaXN5czogcGxvdGluZyBFTEEgbWVhc3VyZQpgYGB7cn0KaW5kZXggPC0gc2VxKDIsOTAsMikKbWVhc3VyZV9yZXN1bHQgPC0gZWxhX21lYXN1cmVzX3Jlc3VsdFtpbmRleF0KZWxhX21lYXN1cmVfZGF0YSA8LSBkYXRhLmZyYW1lKGluZGV4LG1lYXN1cmVfcmVzdWx0KQpuYW1lcyhlbGFfbWVhc3VyZV9kYXRhKSA8LSBjKCdub2lzZV9wb3JjZW50JywnZWxhX21lYXN1cmUnKQoKZ2dwbG90KGVsYV9tZWFzdXJlX2RhdGEpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBub2lzZV9wb3JjZW50LCB5ID0gZWxhX21lYXN1cmUpKSArIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IG5vaXNlX3BvcmNlbnQsIHkgPSBlbGFfbWVhc3VyZSkpCmBgYAoKI1JvYnVzdG5lc3MgQW5hbGlzeXMoMzAgaXRlcmF0aW9ucykKYGBge3J9CnNldC5zZWVkKDMzMSkKcGFydGl0aW9ucyA8LSBzZXEoMiw5MCwyKQoKdG90YWwgPC0gbnJvdyh0cmFpbmluZykKcmVzdWx0X3JmX2FjY3VyYWN5IDwtIGRhdGEuZnJhbWUoJ2NvdW50X29mX2RhdGEnID0gcGFydGl0aW9ucykKcmVzdWx0X2VsYV9tZWFzdXJlIDwtIGRhdGEuZnJhbWUoJ2NvdW50X29mX2RhdGEnID0gcGFydGl0aW9ucykKd3JpdGUudGFibGUocmVzdWx0X3JmX2FjY3VyYWN5LGZpbGU9InRlc3RfcmVzdWx0X3JmX2FjY3VyYWN5LnR4dCIsc2VwPSJ8Iiwgcm93Lm5hbWVzID0gRikKd3JpdGUudGFibGUocmVzdWx0X2VsYV9tZWFzdXJlLGZpbGU9InRlc3RfcmVzdWx0X2VsYV9tZWFzdXJlLnR4dCIsc2VwPSJ8Iiwgcm93Lm5hbWVzID0gRikKZm9yIChqIGluIGMoMTozMCkpewogIHJmX21lYXN1cmVzX3Jlc3VsdCA8LSBjKCkKICBlbGFfbWVhc3VyZXNfcmVzdWx0IDwtIGMoKQogICNDYWxjdWxhbmRvIFJGIHBlcmZvcm1hbmNlIGNvbiB2YXJpYWNpb25lcyBlbiBlbCBwb3JjaWVudG8gZGUgcnVpZG8gZW4gdHJhaW5pbmcKICBmb3IoaSBpbiBwYXJ0aXRpb25zKXsKICAgIHBvcmNlbnQgPC0gKGkqdG90YWwpIC8gMTAwCiAgICB0cmFpbmluZ19ub2lzeSA8LSBnZW5lcmF0ZV9kYXRhX25vaXN5KG5vaXN5X2RhdGEscG9yY2VudCkKICAgIGJhbGFuY2VkX2FjY3VyYWN5X2F1eCA8LSByYW5kb21Gb3Jlc3RfcGVyZm9ybWFjZSh0cmFpbmluZ19ub2lzeSx0ZXN0aW5nKQogICAgcmZfbWVhc3VyZXNfcmVzdWx0W2ldIDwtIGJhbGFuY2VkX2FjY3VyYWN5X2F1eAogIH0KICAjQ2FsY3VsYW5kbyBFTEEgbWVhc3VyZQogIGZvcihpIGluIHBhcnRpdGlvbnMpewogICAgYmFsYW5jZWRfYWNjdXJhY3lfbm9pc3kgPC0gcmZfbWVhc3VyZXNfcmVzdWx0W2ldCiAgICBlbGFfbWVhc3VyZXNfcmVzdWx0W2ldIDwtIGdldF9FTEFfbWVhc3VyZShBMCA9IGJhbGFuY2VkX2FjY3VyYWN5WydCYWxhbmNlZCBBY2N1cmFjeSddLCBBeCA9IGJhbGFuY2VkX2FjY3VyYWN5X25vaXN5KQogIH0KICAKICAjUXVlZGFuZG9tZSBjb24gbGFzIHBvc2ljaW9uZXMgcGFyZXMgcXVlIHNvbiBsYXMgcXVlIHRpZW5lbiBsb3MgdmFsb3JlcwogIGVsYV9tZWFzdXJlc19yZXN1bHQgPC0gZWxhX21lYXN1cmVzX3Jlc3VsdFtwYXJ0aXRpb25zXQogIHJmX21lYXN1cmVzX3Jlc3VsdCA8LSByZl9tZWFzdXJlc19yZXN1bHRbcGFydGl0aW9uc10KICAKICBjb2x1bV9uYW1lIDwtIHBhc3RlKCdpdGVyYXRpb25fJyx0b1N0cmluZyhqKSxzZXAgPSAiIikKICByZXN1bHRfcmZfYWNjdXJhY3kgPC0gY2JpbmQocmVzdWx0X3JmX2FjY3VyYWN5LCBjb2x1bV9uYW1lID0gcmZfbWVhc3VyZXNfcmVzdWx0KQogIHJlc3VsdF9lbGFfbWVhc3VyZSA8LSBjYmluZChyZXN1bHRfZWxhX21lYXN1cmUsIGNvbHVtX25hbWUgPSBlbGFfbWVhc3VyZXNfcmVzdWx0KQogIAogIHdyaXRlLnRhYmxlKHJlc3VsdF9yZl9hY2N1cmFjeSxmaWxlPSJ0ZXN0X3Jlc3VsdF9yZl9hY2N1cmFjeS50eHQiLHNlcD0ifCIsIHJvdy5uYW1lcyA9IEYpCiAgd3JpdGUudGFibGUocmVzdWx0X2VsYV9tZWFzdXJlLGZpbGU9InRlc3RfcmVzdWx0X2VsYV9tZWFzdXJlLnR4dCIsc2VwPSJ8Iiwgcm93Lm5hbWVzID0gRikKfQoKcmVzdWx0X2VsYV9tZWFzdXJlCnJlc3VsdF9yZl9hY2N1cmFjeQoKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD04fQp4IDwtIGMoJ25vc2lzeV9wb3JjZW50JykKZm9yKGkgaW4gYygxOjMwKSl7CiB4W2krMV0gPC0gcGFzdGUoJ2l0ZXJhdGlvbl8nLHRvU3RyaW5nKGkpLHNlcCA9ICIiKQp9CngKcmVzdWx0X2VsYV9tZWFzdXJlLmJrcCA8LSByZXN1bHRfZWxhX21lYXN1cmUKcmVzdWx0X3JmX2FjY3VyYWN5LmJrcCA8LSByZXN1bHRfcmZfYWNjdXJhY3kKbmFtZXMocmVzdWx0X2VsYV9tZWFzdXJlKSA8LSB4Cm5hbWVzKHJlc3VsdF9yZl9hY2N1cmFjeSkgPC0geAoKcmVzdWx0X3JmX2FjY3VyYWN5CmdncGxvdChyZXN1bHRfcmZfYWNjdXJhY3kgJT4lIGdhdGhlcigiaXRlcmF0aW9uIiwidmFsdWUiLDI6MzEpKSsKICBnZW9tX2JveHBsb3QoYWVzKHg9YXMuZmFjdG9yKG5vc2lzeV9wb3JjZW50KSx5PXZhbHVlKSkrCiAgc3RhdF9zdW1tYXJ5KGZ1bi55PSAibWVhbiIsCiAgICAgICAgICAgICAgICAgYWVzKHg9YXMuZmFjdG9yKG5vc2lzeV9wb3JjZW50KSx5PXZhbHVlLGNvbG9yPXZhbHVlKSkKICAjc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICJyZWQiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiLCBtaWRwb2ludCA9IDAuNSkKYGBgCgojQ29zaW5lIFNpbWlsYXJpdHkKYGBge3J9CnRyYWluaW5nCnRlc3RpbmcKcHJlZGljdGlvbl92ZWN0b3IgPC0gdGVzdGluZ1sxLF0KcHJlZGljdGlvbl92ZWN0b3IgPC0gYXMudmVjdG9yKGFzLm1hdHJpeChwcmVkaWN0aW9uX3ZlY3RvcikpCnJlc3VsdCA8LSBwcmVkaWN0aW9uX2J5X3NpbWlsYXJpdHkodHJhaW5pbmcscHJlZGljdGlvbl92ZWN0b3IsMTAwKQpyZXN1bHQKCnJlc3VsdCA8LSBjKCkKZm9yKGkgaW4gMTpucm93KHRlc3RpbmcpKXsKICBwcmVkaWN0aW9uX3ZlY3RvciA8LSB0ZXN0aW5nW2ksXQogIHByZWRpY3Rpb25fdmVjdG9yIDwtIGFzLnZlY3Rvcihhcy5tYXRyaXgocHJlZGljdGlvbl92ZWN0b3IpKQogIHJlc3VsdFtpXSA8LSBwcmVkaWN0aW9uX2J5X3NpbWlsYXJpdHkodHJhaW5pbmcscHJlZGljdGlvbl92ZWN0b3IsMTAwKQp9CnZlY3Rvcl9yZXN1bHQgPC0gdW5saXN0KHJlc3VsdCkKY20gPC0gY29uZnVzaW9uTWF0cml4KHZlY3Rvcl9yZXN1bHQsdGVzdGluZyRjbGFzcykKY20KYGBgCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAKYGBge3J9CmNzX2RhdGEucmVzdWx0IDwtIGRhdGEuZnJhbWUocmZfcmVzdWx0XzUkb3V0cHV0JGRhdGFfY291bnQpCiNmb3IoaSBpbiBjKDE6MSkpewogIGN1cnJlbnRfc2VlZCA8LSAyMjYgIysgaQogIHNldC5zZWVkKGN1cnJlbnRfc2VlZCkKICAKICBzaXplX3RyYWluaW5nIDwtIG5yb3codHJhaW5pbmcpCiAgY3NfdHJhaW5pbmcuc2FtcGxlZF9jdXJyZW50IDwtIHRyYWluaW5nW3NhbXBsZShzaXplX3RyYWluaW5nLCBzaXplX3RyYWluaW5nKSwgXQogIHNwbGl0X3NpemVfdHJhaW5pbmcgPSBzaXplX3RyYWluaW5nIC8gMjAwCiAgbWV0cmljIDwtIG51bWVyaWMoc3BsaXRfc2l6ZV90cmFpbmluZykKICBmb3IoaiBpbiAxOnNwbGl0X3NpemVfdHJhaW5pbmcpewogICAgY291bnQgPC0gMjAwICogagogICAgYXV4X3RyYWluaW5nX3NldCA8LSBjc190cmFpbmluZy5zYW1wbGVkX2N1cnJlbnRbYygxOmNvdW50KSwgXQogICAgcmVzdWx0IDwtIGMoKQogICAgZm9yKGsgaW4gMTpucm93KHRlc3RpbmcpKXsKICAgICAgcHJlZGljdGlvbl92ZWN0b3IgPC0gdGVzdGluZ1trLF0KICAgICAgcHJlZGljdGlvbl92ZWN0b3IgPC0gYXMudmVjdG9yKGFzLm1hdHJpeChwcmVkaWN0aW9uX3ZlY3RvcikpCiAgICAgIG91dHB1dF9yZXN1bHQgPC0gcHJlZGljdGlvbl9ieV9zaW1pbGFyaXR5KGF1eF90cmFpbmluZ19zZXQscHJlZGljdGlvbl92ZWN0b3IsMTAxKQogICAgICByZXN1bHRba10gPC0gb3V0cHV0X3Jlc3VsdAogICAgfQogICAgdmVjdG9yX3Jlc3VsdCA8LSB1bmxpc3QocmVzdWx0KQogICAgY20gPC0gY29uZnVzaW9uTWF0cml4KHZlY3Rvcl9yZXN1bHQsdGVzdGluZyRjbGFzcykKICAgIG1ldHJpY1tqXSA8LSBjbSRieUNsYXNzWydGMSddCiAgICAKICB9CiAgY3NfZGF0YS5yZXN1bHQgPC0gY2JpbmQoY3NfZGF0YS5yZXN1bHQsIG1ldHJpYykKI30KY3NfZGF0YS5yZXN1bHQKYGBgCgpgYGB7cn0KCmBgYA==